Мне нужны предложения по удалению спецификации из файла UTF-8 и созданию копии остальной части xml файла.
Как удалить спецификацию из файла XML в Java
Ответ 1
Наличие разрыва инструмента из-за спецификации в файле UTF-8 - это очень общая вещь в моем опыте. Я не знаю, почему там, где так много downvotes (но тогда это дает мне шанс попытаться получить достаточное количество голосов, чтобы выиграть специальный значок SO;)
Более серьезно: спецификация UTF-8 обычно не имеет такого смысла, но она полностью соответствует (хотя и не рекомендуется) спецификациями. Теперь проблема в том, что многие люди не знают, что спецификация действительна в UTF-8 и, следовательно, написала неработающие инструменты /API, которые не обрабатывают корректно эти файлы.
Теперь у вас могут быть две разные проблемы: вы можете обрабатывать файл с Java или вам нужно использовать Java для программного создания/исправления файлов, которые нужны другим (поврежденным) инструментам.
У меня было дело на одном консультационном концерте, где служба поддержки продолжала получать сообщения от пользователей, у которых были проблемы с каким-то текстовым редактором, который испортил бы совершенно правильные файлы UTF-8, созданные Java. Поэтому мне пришлось обойти эту проблему, убедившись, что вы удаляете спецификацию из каждого файла UTF-8, с которым мы имеем дело.
Я хочу удалить спецификацию из файла, вы можете создать новый файл и пропустить первые три байта. Например:
... $ 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.
Вот метод, который берет файл и копирует его, пропуская первые три байта:
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();
}
}
}
Обратите внимание, что это "raw": вам обычно нужно сначала убедиться, что у вас есть спецификация перед вызовом этого или "Плохие мысли могут произойти" [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 ...
И спецификация исчезла...
Теперь, если вы просто хотите прозрачно удалить спецификацию для своего сломанного Java-API, вы можете использовать описанный здесь pushbackInputStream: почему org.apache.xerces.parsers.SAXParser делает не пропускать спецификацию в закодированном utf8 xml?
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; }
Обратите внимание, что это работает, но определенно НЕ исправить более серьезную проблему, когда у вас могут быть другие инструменты в рабочей цепочке, которые не работают с UTF-8 файлами, имеющими спецификацию.
А вот ссылка на вопрос с более полным ответом, охватывающая другие кодировки: