如何在 Java 中从 XML 文件中删除 BOM
我需要有关如何从 UTF-8 文件中删除 BOM 并创建 xml 文件其余部分的副本的建议。
我需要有关如何从 UTF-8 文件中删除 BOM 并创建 xml 文件其余部分的副本的建议。
根据我的经验,由于 UTF-8 文件中的 BOM 而导致工具损坏是一件非常普遍的事情。我不知道为什么有这么多的反对票(但它让我有机会尝试获得足够的选票来赢得一个特殊的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,然后再将其称为“坏想法可能发生”[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文件。
这里有一个指向一个问题的链接,其中包含更完整的答案,还涵盖了其他编码: