Приложение Java: невозможно правильно прочитать закодированный файл iso-8859-1 - PullRequest
6 голосов
/ 31 января 2009

У меня есть файл, который закодирован как 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);
}

Однако символ ô всегда искажен, обычно печатается как? .

Я прочитал вокруг предмета (и немного узнал в пути), например.

но все равно не получается заставить это работать

Интересно, что это работает на моем локальном компьютере (xp), но не на моем linux box.

Я проверил, поддерживает ли мой jdk требуемые кодировки (они стандартные, так что это не удивительно), используя:

System.out.println(java.nio.charset.Charset.availableCharsets());

Ответы [ 5 ]

14 голосов
/ 31 января 2009

Я подозреваю, что либо ваш файл не на самом деле , закодированный как ISO-8859-1, либо System.out не знает, как напечатать символ.

Рекомендую, чтобы для проверки первого вы исследовали соответствующий байт в файле. Чтобы проверить второй, изучите соответствующий символ в строке, распечатав его с

 System.out.println((int) s.getCharAt(index));

В обоих случаях результат должен быть 244 десятичным; 0xf4 hex.

См. мою статью об отладке Unicode для общего совета (представленный код написан на C #, но его легко конвертировать в Java, и принципы те же).

В общем, кстати, я бы обернул поток InputStreamReader с правильной кодировкой - это проще, чем создавать новые строки "вручную". Я понимаю, что это может быть просто демонстрационный код.

РЕДАКТИРОВАТЬ: Вот действительно простой способ доказать, будет ли работать консоль:

 System.out.println("Here's the character: \u00f4");
9 голосов
/ 31 января 2009

Анализ файла как блоков байтов фиксированного размера не годится - что, если какой-то символ имеет байтовое представление, которое располагается между двумя блоками? Вместо этого используйте 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), чтобы увидеть, есть ли разница.

6 голосов
/ 31 января 2009

@ 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
? & # X00F4;

Если я переключаю кодировку терминала на ISO 8859-1, это печатается:

UTF-8
& # X00F4; & # x00C3; & # x00B4;

В обоих случаях программа Java выдает одинаковые байты:

5554 462d 380a f4c3 b40a

Единственное отличие состоит в том, как терминал интерпретирует полученные байты. В ИСО 8859-1, & # x00F4; кодируется как 0xF4. В UTF-8 & # x00F4; кодируется как 0xC3B4. Другие символы являются общими для обеих кодировок.

3 голосов
/ 31 января 2009

Если вы можете, попробуйте запустить вашу программу в отладчике, чтобы увидеть, что находится внутри вашей строки после ее создания. Возможно, он имеет правильное содержимое, но вывод искажается после вызова System.out.println (s). В этом случае, вероятно, существует несоответствие между тем, что Java считает кодировкой вашего вывода, и кодировкой символов вашего терминала / консоли в Linux.

1 голос
/ 31 января 2009

Обычно, если он работает на вашем локальном ПК с XP, но не в Linux, и вы анализируете один и тот же файл (т. Е. Передали его в двоичном виде между коробками), то это, вероятно, связано с Системой. .out.println вызов. Я не знаю, как вы проверяете вывод, но если вы делаете это, подключаясь к удаленной оболочке из коробки XP, тогда нужно учитывать набор символов оболочки (и клиента).

Кроме того, то, что предлагает Зак Скривена, также верно: вы не можете предполагать, что можете таким образом создавать строки из кусков данных - либо используйте InputStreamReader, либо сначала прочитайте полные данные в массив (очевидно, что не будет большой файл). Однако, поскольку он работает на XP, я бы рискнул предположить, что в данном конкретном случае это, вероятно, не ваша проблема.

...