web-dev-qa-db-ja.com

JavaでXMLファイルからBOMを削除する方法

UTF-8ファイルからBOMを削除し、残りのxmlファイルのコピーを作成する方法についての提案が必要です。

24
hari

UTF-8ファイルのBOMが原因でツールが壊れることは、私の経験ではveryよくあることです。なぜそこに多くの反対票があるのか​​わかりません(しかし、特別なSOバッジ;)を獲得するのに十分な票を獲得する機会を私に与えます。

もっと真剣に:UTF-8 BOMは通常それほど意味がありません仕様により完全に有効です(ただし、推奨されません)。問題は、BOMがUTF-8で有効であることを多くの人が認識していないため、これらのファイルを正しく処理しない壊れたツール/ APIを作成したことです。

ここで、2つの異なる問題が発生する可能性があります。Javaからファイルを処理するか、またはJavaを使用して、他のファイルをプログラムで作成/修正する必要があります(壊れた)ツールが必要です。

私はあるコンサルティングギグで、Javaによって生成された完全に有効なUTF-8ファイルを台無しにするテキストエディターで問題が発生したユーザーからのメッセージをヘルプデスクが受け取り続けるケースがありました。したがって、私が扱っていたすべてのUTF-8ファイルからBOMを必ず削除することで、この問題を回避する必要がありました。

ファイルからBOMを削除したい場合、新しいファイルを作成して最初の3バイトをスキップできます。例えば:

... $  file  /tmp/src.txt 
/tmp/src.txt: UTF-8 Unicode (with BOM) English text

... $  ls -l  /tmp/src.txt 
-rw-rw-r-- 1 tact tact 1733 2012-03-16 14:29 /tmp/src.txt

... $  hexdump  -C  /tmp/src.txt | head -n 1
00000000  ef bb bf 50 6f 6b 65 ...

ご覧のとおり、ファイルは「ef bb bf」で始まります。これは(完全に有効な)UTF-8 BOMです。

ファイルを受け取り、最初の3バイトをスキップしてそのコピーを作成するメソッドを次に示します。

 public static void workAroundbrokenToolsAndAPIs(File sourceFile, File destFile) throws IOException {
    if(!destFile.exists()) {
        destFile.createNewFile();
    }

    FileChannel source = null;
    FileChannel destination = null;

    try {
        source = new FileInputStream(sourceFile).getChannel();
        source.position(3);
        destination = new FileOutputStream(destFile).getChannel();
        destination.transferFrom( source, 0, source.size() - 3 );
    }
    finally {
        if(source != null) {
            source.close();
        }
        if(destination != null) {
            destination.close();
        }
    }
}

これは「未加工」であることに注意してください。通常、これを呼び出す前に、まずBOMがあることを確認するか、「悪い考えが起こる」[TM]を呼び出します。

後でファイルを確認できます。

... $  file  /tmp/dst.txt 
/tmp/dst.txt: UTF-8 Unicode English text

... $  ls -l  /tmp/dst.txt 
-rw-rw-r-- 1 tact tact 1730 2012-03-16 14:41 /tmp/dst.txt

... $  hexdump -C /tmp/dst.txt
00000000  50 6f 6b 65 ...

そして、BOMはなくなりました...

ここで、壊れたJava APIのBOMを透過的に削除したい場合は、ここで説明されているpushbackInputStreamを使用できます。 なぜorg.Apache.xerces.parsers.SAXParserはutf8エンコードされたxmlでBOMをスキップしないのですか?

private static InputStream checkForUtf8BOMAndDiscardIfAny(InputStream inputStream) throws IOException {
    PushbackInputStream pushbackInputStream = new PushbackInputStream(new BufferedInputStream(inputStream), 3);
    byte[] bom = new byte[3];
    if (pushbackInputStream.read(bom) != -1) {
        if (!(bom[0] == (byte) 0xEF && bom[1] == (byte) 0xBB && bom[2] == (byte) 0xBF)) {
            pushbackInputStream.unread(bom);
        }
    }
    return pushbackInputStream; }

これは機能しますが、間違いなく[〜#〜] [〜#〜]は、他のツールをBOMのあるUTF-8ファイルでワークチェーンが正しく機能しない。

そして、他のエンコーディングもカバーする、より完全な答えのある質問へのリンクがあります:

バイトオーダーマークはJavaでのファイル読み取りを台無しにします

37
TacticalCoder