Как удалить спецификацию из XML-файла в Java - PullRequest
23 голосов
/ 16 марта 2012

Мне нужны предложения о том, как удалить спецификацию из файла UTF-8 и создать копию остальной части XML-файла.

1 Ответ

35 голосов
/ 16 марта 2012

Наличие взлома инструмента из-за спецификации в файле UTF-8 - очень обычная вещь в моем опыте.Я не знаю, почему там, где так много отрицательных голосов (но тогда это дает мне возможность попытаться набрать достаточное количество голосов, чтобы выиграть специальный знак 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();
        }
    }
}

Обратите внимание, что он «сырой»: обычно вам нужно сначала убедиться, что у вас есть спецификация, прежде чем вызывать эту или «Плохую»Думает, что может произойти "[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?1031 *

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, имеющимиСпецификация.

А вот ссылка на вопрос с более полным ответом, включая и другие кодировки:

Порядок следования байтов портит чтение файлов в Java

...