Проблема с кодировкой Java FileReader

130

Я попытался использовать java.io.FileReader для чтения некоторых текстовых файлов и преобразования их в строку, но обнаружил, что результат неверно закодирован и вообще не читается.

Вот моя среда:

  • Windows 2003, кодировка ОС: CP1252

  • Java 5.0

Мои файлы имеют кодировку UTF-8 или CP1252, и некоторые из них (файлы с кодировкой UTF-8) могут содержать китайские (нелатинские) символы.

Я использую следующий код для работы:

   private static String readFileAsString(String filePath)
    throws java.io.IOException{
        StringBuffer fileData = new StringBuffer(1000);
        FileReader reader = new FileReader(filePath);
        //System.out.println(reader.getEncoding());
        BufferedReader reader = new BufferedReader(reader);
        char[] buf = new char[1024];
        int numRead=0;
        while((numRead=reader.read(buf)) != -1){
            String readData = String.valueOf(buf, 0, numRead);
            fileData.append(readData);
            buf = new char[1024];
        }
        reader.close();
        return fileData.toString();
    }

Приведенный выше код не работает. Я обнаружил, что кодировка FileReader - CP1252, даже если текст закодирован в UTF-8. Но JavaDoc java.io.FileReader говорит, что:

Конструкторы этого класса предполагают, что кодировка символов по умолчанию и размер байтового буфера по умолчанию подходят.

Означает ли это, что мне не нужно самостоятельно устанавливать кодировку символов, если я использую FileReader? Но в настоящее время я получил неверно закодированные данные, как правильно поступить в моей ситуации? Спасибо.

nybon
источник
Вы также должны потерять String.valueOf () внутри цикла и напрямую использовать StringBuffer.append (char [], int, int). Это позволяет сэкономить на копировании char []. Также замените StringBuffer на StringBuilder. Но все это не связано с вашим вопросом ».
Joachim Sauer,
1
Ненавижу это говорить, но читали ли вы JavaDoc сразу после вставленной части? Вы знаете, что часть, которая говорит: «Чтобы указать эти значения самостоятельно, создайте InputStreamReader на FileInputStream»?
Powerlord
Спасибо за ваш комментарий, на самом деле я читал JavaDoc, но я не уверен, должен ли я сам указывать эти значения и переключаться на «создание InputStreamReader на FileInputStream».
nybon
Да, если вы знаете, что файл находится в кодировке, отличной от кодировки по умолчанию для платформы, вы должны указать InputStreamReader, какую из них использовать.
Алан Мур,

Ответы:

248

Да, вам нужно указать кодировку файла, который вы хотите прочитать.

Да, это означает, что вы должны знать кодировку файла, который хотите прочитать.

Нет, не существует общего способа угадать кодировку любого заданного «текстового» файла.

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

Поскольку в Java 11 FileReaderпоявились конструкторы, принимающие кодировку: new FileReader(file, charset)и new FileReader(fileName, charset).

В более ранних версиях java вам нужно использовать .new InputStreamReader(new FileInputStream(pathToFile), <encoding>)

Иоахим Зауэр
источник
1
InputStream = новый FileInputStream (имя файла); здесь у меня ошибка файл не найден ошибка с русским именем файла
Бхану Шарма
3
+1 за предложение использовать InputStreamReader, однако использование ссылок в блоках кода затрудняет копирование и вставку кода, если это можно изменить,
спасибо
1
Будет ли это "UTF-8" или "UTF8" в кодировках. Согласно справочнику Java SE по кодированию , поскольку InputStreamReaderэто java.ioкласс, он будет «UTF8»?
NobleUplift
9
@NobleUplift: самая безопасная ставка StandardCharsets.UTF_8, там нет шанса опечатки ;-) Но да, если вы выберете строку "UTF8", будет правильно (хотя я, кажется, помню, что она принимает оба варианта).
Joachim Sauer
1
@JoachimSauer На самом деле, это одна из целей Byte Order Mark, наряду с ... ну ... установлением порядка байтов! :) Поэтому мне кажется странным, что Java FileReader не может автоматически определять UTF-16 с такой спецификацией ... Фактически, я однажды написал a, UnicodeFileReaderкоторый делает именно это. К сожалению, с закрытым исходным кодом, но у Google есть UnicodeReader, который очень похож.
Stijn de Witt
79

FileReader использует кодировку платформы Java по умолчанию, которая зависит от системных настроек компьютера, на котором она работает, и обычно является самой популярной кодировкой среди пользователей в этой локали.

Если это «лучшее предположение» неверно, вам необходимо явно указать кодировку. К сожалению, FileReaderне позволяет этого (серьезная оплошность в API). Вместо этого вы должны использовать new InputStreamReader(new FileInputStream(filePath), encoding)и, в идеале, получить кодировку из метаданных файла.

Майкл Боргвардт
источник
24
«серьезный недосмотр в API» - спасибо за это объяснение - мне было интересно, почему я не могу найти конструктор, который искал! Cheers John
monojohnny
@Bhanu Sharma: это проблема с кодировкой на другом уровне, проверьте, откуда вы берете имя файла, и если это жестко запрограммировано, какую кодировку использует компилятор.
Майкл Боргвардт
1
@BhanuSharma: проблемы с кодировкой имен файлов не имеют ничего общего с этим вопросом. См. Один из многих существующих вопросов «почему имена файлов Unicode не работают в Java». Спойлер: API-интерфейсы java.io, такие как FileReader, используют вызовы файловой системы стандартной библиотеки C, которая не поддерживает Unicode в Windows; рассмотрите возможность использования вместо этого java.nio.
bob
1
« FileReaderиспользует кодировку по умолчанию для платформы Java, которая зависит от системных настроек компьютера, на котором она работает, и обычно является самой популярной кодировкой среди пользователей в этом регионе». Я бы так не сказал. По крайней мере винды. По некоторым странным техническим / историческим причинам JVM игнорирует тот факт, что Unicode является рекомендуемой кодировкой в ​​Windows для «всех новых приложений», и вместо этого всегда действует так, как если бы устаревшая кодировка, настроенная как резерв для устаревших приложений , была «платформой по умолчанию».
Stijn de Witt
6
Я бы даже сказал, что если ваше приложение Java явно не указывает кодировки каждый раз, когда оно читает или записывает в файлы / потоки / ресурсы, оно сломано , потому что тогда оно никогда не может работать надежно.
Stijn de Witt
8

Начиная с Java 11 вы можете использовать это:

public FileReader(String fileName, Charset charset) throws IOException;
Радослав Иванов
источник
6

Для Java 7+ документ вы можете использовать это:

BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);

Вот все кодировки doc

Например, если ваш файл находится в CP1252, используйте этот метод

Charset.forName("windows-1252");

Вот другие канонические имена для Java кодировок и для ввода - вывода и NIO документ

Если вы не знаете , с точно кодирующим у вас есть в файл, вы можете использовать некоторый сторонний LIBS как этот инструмент от Google это , который работает довольно аккуратно.

Андреас Гелевер
источник
1

FileInputStream с InputStreamReader лучше, чем напрямую с помощью FileReader, потому что последний не позволяет вам указывать кодировку кодировки.

Вот пример использования BufferedReader, FileInputStream и InputStreamReader вместе, чтобы вы могли читать строки из файла.

List<String> words = new ArrayList<>();
List<String> meanings = new ArrayList<>();
public void readAll( ) throws IOException{
    String fileName = "College_Grade4.txt";
    String charset = "UTF-8";
    BufferedReader reader = new BufferedReader(
        new InputStreamReader(
            new FileInputStream(fileName), charset)); 

    String line; 
    while ((line = reader.readLine()) != null) { 
        line = line.trim();
        if( line.length() == 0 ) continue;
        int idx = line.indexOf("\t");
        words.add( line.substring(0, idx ));
        meanings.add( line.substring(idx+1));
    } 
    reader.close();
}
Гуантун Шен
источник
0

Для других латинских языков, например кириллица, вы можете использовать что-то вроде этого:

FileReader fr = new FileReader("src/text.txt", StandardCharsets.UTF_8);

и убедитесь, что ваш .txtфайл сохранен UTF-8в ANSIформате (но не по умолчанию ). Ура!

Ефименко Ивгун
источник