Приложение Java: невозможно правильно прочитать закодированный файл iso-8859-1
У меня есть файл, который закодирован как ISO-8859-1 и содержит такие символы, как ô .
Я читаю этот файл с кодом Java, что-то вроде:
File in = new File("myfile.csv");
InputStream fr = new FileInputStream(in);
byte[] buffer = new byte[4096];
while (true) {
int byteCount = fr.read(buffer, 0, buffer.length);
if (byteCount <= 0) {
break;
}
String s = new String(buffer, 0, byteCount,"ISO-8859-1");
System.out.println(s);
}
Однако символ ô всегда искажен, обычно печатается как?,
Я прочитал вокруг предмета (и узнал немного по дороге), например,
- http://www.joelonsoftware.com/articles/Unicode.html
- http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058
- http://www.ingrid.org/java/i18n/utf-16/
но до сих пор не могу заставить это работать
Интересно, что это работает на моем локальном компьютере (xp), но не на моей Linux-коробке.
Я проверил, что мой jdk поддерживает требуемые кодировки (они стандартные, так что это не удивительно), используя:
System.out.println(java.nio.charset.Charset.availableCharsets());
5 ответов
Я подозреваю, что либо ваш файл на самом деле не закодирован как ISO-8859-1, либо System.out не знает, как напечатать символ.
Я рекомендую, чтобы проверить первый, вы изучите соответствующий байт в файле. Чтобы проверить второй, изучите соответствующий символ в строке, распечатав его с
System.out.println((int) s.getCharAt(index));
В обоих случаях результат должен быть 244 десятичных; 0xf4 гекс.
См. Мою статью об отладке Unicode для общего совета (представленный код написан на C#, но его легко конвертировать в Java, и принципы те же).
В общем, кстати, я бы обернул поток InputStreamReader
с правильной кодировкой - это проще, чем создавать новые строки "вручную". Я понимаю, что это может быть просто демо-код, хотя.
РЕДАКТИРОВАТЬ: Вот действительно простой способ доказать, будет ли работать консоль:
System.out.println("Here's the character: \u00f4");
Разбирать файл как блоки байтов фиксированного размера нехорошо - что если какой-то символ имеет байтовое представление, которое располагается между двумя блоками? Используйте InputStreamReader
с соответствующей кодировкой символов:
BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream("myfile.csv"), "ISO-8859-1");
char[] buffer = new char[4096]; // character (not byte) buffer
while (true)
{
int charCount = br.read(buffer, 0, buffer.length);
if (charCount == -1) break; // reached end-of-stream
String s = String.valueOf(buffer, 0, charCount);
// alternatively, we can append to a StringBuilder
System.out.println(s);
}
Кстати, не забудьте проверить, что символ Юникода действительно может отображаться правильно. Вы также можете перенаправить вывод программы в файл, а затем сравнить его с исходным файлом.
Как предполагает Джон Скит, проблема также может быть связана с консолью. Пытаться System.console().printf(s)
чтобы увидеть, есть ли разница.
@Joel - ваш собственный ответ подтверждает, что проблема заключается в разнице между кодировкой по умолчанию в вашей операционной системе (UTF-8, которую выбрал Java) и кодировкой, используемой вашим терминалом (ISO-8859-1).
Рассмотрим этот код:
public static void main(String[] args) throws IOException {
byte[] data = { (byte) 0xF4 };
String decoded = new String(data, "ISO-8859-1");
if (!"\u00f4".equals(decoded)) {
throw new IllegalStateException();
}
// write default charset
System.out.println(Charset.defaultCharset());
// dump bytes to stdout
System.out.write(data);
// will encode to default charset when converting to bytes
System.out.println(decoded);
}
По умолчанию мой терминал Ubuntu (8.04) использует кодировку UTF-8. С этой кодировкой это напечатано:
UTF-8,
?ô
Если я переключаю кодировку терминала на ISO 8859-1, это печатается:
UTF-8,
ôÃ'
В обоих случаях те же байты испускаются программой Java:
5554 462d 380a f4c3 b40a
Разница лишь в том, как терминал интерпретирует полученные байты. В ISO 8859-1, ô кодируется как 0xF4. В UTF-8 ô кодируется как 0xC3B4. Другие символы являются общими для обеих кодировок.
Если вы можете, попробуйте запустить вашу программу в отладчике, чтобы увидеть, что находится внутри вашей строки после ее создания. Возможно, он имеет правильное содержимое, но вывод искажается после вызова System.out.println (s). В этом случае, вероятно, существует несоответствие между тем, что Java считает кодировкой вашего вывода, и кодировкой символов вашего терминала / консоли в Linux.
По сути, если он работает на вашем локальном ПК с XP, но не в Linux, и вы анализируете один и тот же файл (т. Е. Вы передали его в двоичном виде между коробками), то он, вероятно, как-то связан с System.out. распечатать звонок. Я не знаю, как вы проверяете вывод, но если вы делаете это, подключаясь к удаленной оболочке из коробки XP, то нужно рассмотреть набор символов оболочки (и клиента).
Кроме того, то, что предлагает Зак Скривена, также верно: вы не можете предполагать, что вы можете создавать строки из кусков данных таким образом - либо использовать InputStreamReader, либо сначала считывать полные данные в массив (очевидно, не будет работать для большого файла), Однако, поскольку он работает на XP, я бы рискнул предположить, что в данном конкретном случае это, вероятно, не ваша проблема.