如何从Java中删除XML文件中的BOM

我需要有关从UTF-8文件中删除BOM的方法的建议,并创建其余xml文件的副本。

根据我的经验,由于UTF-8文件中的BOM而导致工具崩溃是非常常见的事情。 我不知道为什么会有这么多的downvotes(但它让我有机会获得足够的投票来赢得一个特殊的SO徽章;)

更严重的是:UTF-8 BOM通常没有多大意义, 规格完全有效(尽管不鼓励)。 现在的问题是很多人都不知道BOM在UTF-8中是有效的,因此编写了不能正确处理这些文件的破坏的工具/ API。

现在您可能有两个不同的问题:您可能希望从Java处理文件,或者您需要使用Java以编程方式创建/修复其他(损坏的)工具所需的文件。

我已经在一个咨询工具中遇到过这样的情况:帮助台会不断收到来自用户的消息,这些用户会遇到一些文本编辑器问题,这些编辑器会破坏Java生成的完全有效的UTF-8文件。 所以我必须通过确保从我们正在处理的每个UTF-8文件中删除BOM来解决该问题。

我想从文件中删除BOM,您可以创建一个新文件并跳过前三个字节。 例如:

... $ 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。

这是一个获取文件并通过跳过前三个字节来复制它的方法:

  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或“Bad Thinks May Happen”[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解压缩文件读取