Проблема кодування 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? Але я в даний час отримав неправильно закодовані дані, що є правильним способом поводження з моєю ситуацією? Дякую.


Також слід втратити String.valueOf () всередині циклу і безпосередньо використовувати StringBuffer.append (char [], int, int). Це економить багато копіювання символу []. Також замініть StringBuffer на StringBuilder. Нічого з цього питання не стосується вашого питання ".
Йоахім Зауер

1
Я ненавиджу це говорити, але чи читали ви JavaDoc відразу після частини, яку ви вставили? Ви знаєте, частина, в якій написано "Щоб самостійно вказати ці значення, побудуйте InputStreamReader на FileInputStream."
Powerlord

Дякую за ваш коментар, насправді я читав JavaDoc, але я не впевнений, чи варто мені сам вказати ці значення та перейти до "побудови InputStreamReader на FileInputStream".
нібонь

Так, якщо ви знаєте, що файл знаходиться в чомусь іншому, ніж кодування платформи за замовчуванням, ви повинні сказати InputStreamReader, який саме використовувати.
Алан Мур

Відповіді:


248

Так, вам потрібно вказати кодування файлу, який ви хочете прочитати.

Так, це означає, що ви повинні знати кодування файлу, який ви хочете прочитати.

Ні, немає загального способу відгадати кодування будь-якого файлу "звичайний текст".

Конструктори з одним аргументомFileReader завжди використовують кодування платформи за замовчуванням, що, як правило, погана ідея .

Оскільки Java 11 FileReaderтакож отримала конструктори, які приймають кодування: new FileReader(file, charset)і new FileReader(fileName, charset).

У попередніх версіях Java потрібно використовувати .new InputStreamReader(new FileInputStream(pathToFile), <encoding>)


1
InputStream є = новий FileInputStream (назва файлу); тут я отримав файл помилки не знайдено помилку з російською назвою файлу
Bhanu Sharma

3
+1 за пропозицію використовувати InputStreamReader, однак використання посилань у кодових блоках ускладнює копіювання та вставлення коду, якщо це можна змінити, thx
Ferrybig

1
Буде це "UTF-8" або "UTF8" у кодуванні. Відповідно до посилання Java SE щодо кодування , оскільки InputStreamReaderце java.ioклас, це було б "UTF8"?
NobleUplift

9
@NobleUplift: найбезпечніша ставка StandardCharsets.UTF_8, там немає шансів помилитися там ;-) Але так, якщо ви йдете з рядком, "UTF8"було б правильно (хоча я, мабуть, пам’ятаю, що це прийме обидва способи).
Йоахім Зауер

1
@JoachimSauer Насправді, це одна з цілей Byte Order Mark, поряд з .. ну .. встановлення порядку байтів! :) Як таке, мені здається дивним, що FileReader Java не в змозі автоматично виявити UTF-16, у якого є такий BOM ... Насправді я одного разу написав, UnicodeFileReaderщо робить саме це. На жаль, закрите джерело, але Google має UnicodeReader, який дуже схожий.
Штійн де Вітт

79

FileReader використовує кодування за замовчуванням платформи Java, яка залежить від системних налаштувань комп'ютера, на якому вона працює, і, як правило, є найпопулярнішим кодуванням серед користувачів у цій місцевості.

Якщо ця "найкраща здогадка" не вірна, тоді вам потрібно чітко вказати кодування. На жаль, FileReaderце не дозволяє (великий нагляд в API). Натомість ви повинні використовувати new InputStreamReader(new FileInputStream(filePath), encoding)та в ідеалі отримати кодування з метаданих про файл.


24
"великий нагляд в API" - дякую за це пояснення - мені було цікаво, чому я не зміг знайти конструктор, за яким я хотів!
Здравствуйте,

@Bhanu Sharma: це проблема кодування на іншому рівні, перевірте, звідки ви отримуєте ім'я файлу, і чи є жорстким кодом, яке кодування використовує компілятор.
Майкл Боргвардт

1
@BhanuSharma: проблеми кодування імені файлів не мають нічого спільного з цим питанням. Перегляньте одне з багатьох існуючих питань "чому у Java не працюють імена файлів Unicode". Спойлер: API java.io на зразок FileReader використовує стандартні виклики файлової системи бібліотеки C, які не можуть підтримувати Unicode в Windows; розгляньте натомість використання java.nio.
bobince

1
" FileReaderвикористовує кодування за замовчуванням платформи Java, що залежить від системних налаштувань комп'ютера, на якому він працює, і, як правило, є найпопулярнішим кодуванням серед користувачів у цій місцевості." Я б не сказав цього. Принаймні Windows. З якихось дивних технічних та історичних причин JVM ігнорує той факт, що Unicode є рекомендованим кодуванням у Windows для «всіх нових програм», а натомість завжди діє так, ніби застаріле кодування, налаштоване як резервне для застарілих додатків, є «платформою за замовчуванням».
Штійн де Вітт

6
Я б навіть сказав, що якщо у вашій програмі Java чітко не вказано кодування кожного разу, коли вона читається чи записується у файли / потоки / ресурси, вона порушується , тому що вона ніколи не може надійно працювати.
Штійн де Вітт


6

Для Java 7+ doc ви можете використовувати це:

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

Ось усі Charsets doc

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

Charset.forName("windows-1252");

Ось інші канонічні назви кодування Java як для IO, так і для NIO doc

Якщо ви не знаєте , з точно кодує у вас є в файл, ви можете використовувати деякий сторонній 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). Ура!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.