Чтение UTF-8 — маркер спецификации

Я читаю файл через FileReader - файл декодируется UTF-8 (с спецификацией), теперь моя проблема: я читаю файл и вывожу строку, но, к сожалению, маркер спецификации также выводится. Почему это происходит?

fr = new FileReader(file);
br = new BufferedReader(fr);
    String tmp = null;
    while ((tmp = br.readLine()) != null) {
    String text;    
    text = new String(tmp.getBytes(), "UTF-8");
    content += text + System.getProperty("line.separator");
}

вывод после первой строки

?<style>

person onigunn    schedule 04.02.2011    source источник
comment
UTF-8 не должен иметь спецификацию! Это не является необходимым и не рекомендуется стандартом Unicode.   -  person tchrist    schedule 04.02.2011
comment
@tchrist: Microsoft не заботится о стандартах.   -  person Matti Virkkunen    schedule 04.02.2011
comment
Чтобы расширить точку зрения Матти, все текстовые редакторы MS добавляют к документам UTF-8 префикс BOM.   -  person Ant    schedule 04.02.2011
comment
@Matti не рекомендуется != нестандартно   -  person bacar    schedule 31.01.2012
comment
@tchrist скажите это людям, которые помещают спецификацию в файлы UTF-8 (= Microsoft) при их сохранении.   -  person dstibbe    schedule 08.06.2012
comment
@dstibbe Я не несу ответственности за глупость Microsoft. Я не буду участвовать в этом. Мои руки чистые.   -  person tchrist    schedule 08.06.2012
comment
@tchrist Хотел бы я, чтобы все было так просто. Вы создаете приложение для пользователей, а не для себя. И пользователи используют (частично) программное обеспечение Microsoft для создания своих файлов.   -  person dstibbe    schedule 11.06.2012
comment
Спецификация необходима для UTF-16, необязательна для UTF-8. Java не может обрабатывать ни то, ни другое (по стандартной библиотеке). С# может справиться с обоими. Теперь поговорим о том, кто следует стандарту, а кто нет.   -  person peenut    schedule 21.07.2012
comment
@peenut, Java может обрабатывать спецификации в UTF-16, если вы сообщите об этом   -  person finnw    schedule 06.12.2013
comment
@tchriist, спецификация - это стандарт, а не MS или Unicode. peenut прав, что спецификация для UTF-16 в xml-файлах является ДОЛЖНОЙ, а спецификация UTF-8 в xml-файле может быть. xml Standard на W3org w3.org/TR/xml/#charencoding. Методы автоматического определения спецификации не являются нормативным стандартом. Раздел ›F Автоопределение кодировок символов (ненормативное)‹   -  person bernie3280109    schedule 05.02.2020


Ответы (9)


В Java вы должны вручную использовать спецификацию UTF8, если она есть. Это поведение задокументировано в базе данных ошибок Java, здесь и здесь. На данный момент исправления не будет, потому что оно сломает существующие инструменты, такие как анализаторы JavaDoc или XML. оператор ввода-вывода Apache Commons предоставляет BOMInputStream для обработки этой ситуации.

Взгляните на это решение: Обработка файла UTF8 с помощью BOM< /а>

person RealHowTo    schedule 04.02.2011
comment
Очень поздно в игру, но это кажется очень медленным для больших файлов. Пробовал использовать буфер. Если вы используете буфер, он также оставляет какие-то конечные данные. - person rocksNwaves; 11.02.2020

Самое простое исправление, вероятно, просто удалить полученный \uFEFF из строки, так как крайне маловероятно, что он появится по какой-либо другой причине.

tmp = tmp.replace("\uFEFF", "");

См. также отчет об ошибке в Guava.

person finnw    schedule 04.02.2011
comment
Плохая сторона крайне маловероятного заключается в том, что он появляется крайне редко, так что найти ошибку крайне сложно... :) Так что будьте крайне осторожны при использовании этого кода, если вы верите, что ваша программа будет успешной и долгоживущей, потому что раньше или позже произойдет любая существующая ситуация. - person Franz D.; 15.07.2015
comment
FEFF — это спецификация UTF-16. Спецификация UTF-8 — EFBBBF. - person Steve Pitchers; 27.05.2016
comment
@StevePitchers, но мы должны сопоставить его после декодирования, когда он является частью String (который всегда представляется как UTF-16) - person finnw; 27.05.2016
comment
Как насчет \uFFFE (UTF-16, прямой порядок байтов)? - person Suzana; 22.03.2018
comment
@live-love И если в файле нет спецификации, вы просто усекли первую строку. - person Eric Duminil; 08.05.2019
comment
Чтобы убедиться, что вы заменяете спецификацию только в том случае, если она находится в начале строки, вы можете использовать tmp = tmp.replaceAll("\\A\uFEFF", ""); - person Eric Duminil; 08.05.2019

Используйте библиотеку Apache Commons.

Класс: org.apache.commons.io.input.BOMInputStream

Пример использования:

String defaultEncoding = "UTF-8";
InputStream inputStream = new FileInputStream(someFileWithPossibleUtf8Bom);
try {
    BOMInputStream bOMInputStream = new BOMInputStream(inputStream);
    ByteOrderMark bom = bOMInputStream.getBOM();
    String charsetName = bom == null ? defaultEncoding : bom.getCharsetName();
    InputStreamReader reader = new InputStreamReader(new BufferedInputStream(bOMInputStream), charsetName);
    //use reader
} finally {
    inputStream.close();
}
person peenut    schedule 21.12.2012
comment
Этот код будет работать только с обнаружением и исключением спецификации UTF-8. Проверьте реализацию bOMInputStream: ``` /** * Создает новый BOM InputStream, который определяет * {@link ByteOrderMark#UTF_8} и (необязательно) включает его. * @param делегировать InputStream для делегирования * @param include true, чтобы включить спецификацию UTF-8, или * false, чтобы исключить ее */ } ``` - person czupe; 30.08.2017

Вот как я использую Apache BOMInputStream, он использует блок try-with-resources. Аргумент «false» указывает объекту игнорировать следующие спецификации (мы используем текстовые файлы «без спецификаций» из соображений безопасности, ха-ха):

try( BufferedReader br = new BufferedReader( 
    new InputStreamReader( new BOMInputStream( new FileInputStream(
       file), false, ByteOrderMark.UTF_8,
        ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_16LE,
        ByteOrderMark.UTF_32BE, ByteOrderMark.UTF_32LE ) ) ) )
{
    // use br here

} catch( Exception e)

}
person snakedoctor    schedule 25.05.2016
comment
никогда не могу понять, как публиковать материалы на этом сайте - всегда заканчивается AFU. - person snakedoctor; 25.05.2016

Рассмотрим UnicodeReader от Google, который сделает всю эту работу за вас.

Charset utf8 = Charset.forName("UTF-8"); // default if no BOM present
try (Reader r = new UnicodeReader(new FileInputStream(file), utf8)) {
    ....
}

Зависимость Мейвена:

<dependency>
    <groupId>com.google.gdata</groupId>
    <artifactId>core</artifactId>
    <version>1.47.1</version>
</dependency>
person Adrian Smith    schedule 12.02.2018
comment
Спасибо. Он хорошо работает и с SuperCSV. Это принесло мне несколько очков. :) - person Sacky San; 26.05.2020
comment
Отлично. Очень простое решение, которое отлично сработало для OpenCSV. - person grizzasd; 16.11.2020

Используйте ввод-вывод Apache Commons.

Например, давайте посмотрим на мой код (используемый для чтения текстового файла с латинскими и кириллическими символами) ниже:

String defaultEncoding = "UTF-16";
InputStream inputStream = new FileInputStream(new File("/temp/1.txt"));

BOMInputStream bomInputStream = new BOMInputStream(inputStream);

ByteOrderMark bom = bomInputStream.getBOM();
String charsetName = bom == null ? defaultEncoding : bom.getCharsetName();
InputStreamReader reader = new InputStreamReader(new BufferedInputStream(bomInputStream), charsetName);
int data = reader.read();
while (data != -1) {

 char theChar = (char) data;
 data = reader.read();
 ari.add(Character.toString(theChar));
}
reader.close();

В результате у нас есть ArrayList с именем «ari» со всеми символами из файла «1.txt», кроме BOM.

person pawman    schedule 01.07.2017

Упоминается здесь это обычно проблема с файлами в Windows.

Одним из возможных решений может быть запуск файла с помощью такого инструмента, как dos2unix.

person Drake Sobania    schedule 26.02.2017
comment
да, dos2unix (который является частью cygwin) имеет опции для добавления (--add-bom) и удаления (--remove-bom) bom. - person Roman; 17.10.2017

Если кто-то хочет сделать это со стандартом, это будет способ:

public static String cutBOM(String value) {
    // UTF-8 BOM is EF BB BF, see https://en.wikipedia.org/wiki/Byte_order_mark
    String bom = String.format("%x", new BigInteger(1, value.substring(0,3).getBytes()));
    if (bom.equals("efbbbf"))
        // UTF-8
        return value.substring(3, value.length());
    else if (bom.substring(0, 2).equals("feff") || bom.substring(0, 2).equals("ffe"))
        // UTF-16BE or UTF16-LE
        return value.substring(2, value.length());
    else
        return value;
}
person Markus    schedule 20.03.2019

Самый простой способ, который я нашел, чтобы обойти спецификацию

BufferedReader br = new BufferedReader(new InputStreamReader(fis));    
while ((currentLine = br.readLine()) != null) {
                    //case of, remove the BOM of UTF-8 BOM
                    currentLine = currentLine.replace("","");
person David    schedule 26.10.2017