Чтение UTF-8 - спецификация
Я читаю файл через FileReader - файл декодирован в UTF-8 (с BOM), теперь моя проблема: я читаю файл и вывожу строку, но, к сожалению, выводится маркер BOM. Почему это происходит?
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>
8 ответов
В Java вы должны вручную использовать спецификацию UTF8, если она есть. Это поведение задокументировано в базе данных ошибок Java, здесь и здесь. Пока не будет исправлений, потому что это сломает существующие инструменты, такие как JavaDoc или парсеры XML. Apache IO Commons предоставляет BOMInputStream
чтобы справиться с этой ситуацией.
Взгляните на это решение: обработайте файл UTF8 с помощью спецификации
Самое простое исправление - это, вероятно, просто удалить \uFEFF
из строки, так как это крайне маловероятно, чтобы появиться по любой другой причине.
tmp = tmp.replace("\uFEFF", "");
Также см. Этот отчет об ошибке Guava
Используйте библиотеку 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();
}
Вот как я использую 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)
}
Рассмотрим UnicodeReader от Google, который сделает всю эту работу за вас.
Charset utf8 = Charset.forName("UTF-8"); // default if no BOM present
try (Reader r = new UnicodeReader(new FileInputStream(file), utf8)) {
....
}
Зависимость Maven:
<dependency>
<groupId>com.google.gdata</groupId>
<artifactId>core</artifactId>
<version>1.47.1</version>
</dependency>
Используйте Apache Commons IO.
Например, давайте посмотрим на мой код (используемый для чтения текстового файла с латинскими и кириллическими символами) ниже:
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", за исключением спецификации.
Если кто-то хочет сделать это со стандартом, это будет способ:
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;
}
Здесь упоминается, что обычно это проблема с файлами в Windows.
Одним из возможных решений было бы сначала запустить файл через такой инструмент, как dos2unix.
Самый простой способ, который я нашел, чтобы обойти спецификацию
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
while ((currentLine = br.readLine()) != null) {
//case of, remove the BOM of UTF-8 BOM
currentLine = currentLine.replace("","");