Masalah penyandian FileReader Java

130

Saya mencoba menggunakan java.io.FileReader untuk membaca beberapa file teks dan mengonversinya menjadi string, tetapi saya menemukan hasilnya salah dikodekan dan tidak dapat dibaca sama sekali.

Inilah lingkungan saya:

  • Windows 2003, pengkodean OS: CP1252

  • Java 5.0

File saya dikodekan UTF-8 atau dikodekan CP1252, dan beberapa di antaranya (file yang dikodekan UTF-8) dapat berisi karakter China (non-Latin).

Saya menggunakan kode berikut untuk melakukan pekerjaan saya:

   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();
    }

Kode di atas tidak berfungsi. Saya menemukan pengkodean FileReader adalah CP1252 bahkan jika teksnya dikodekan UTF-8. Tetapi JavaDoc dari java.io.FileReader mengatakan bahwa:

Konstruktor kelas ini mengasumsikan bahwa pengkodean karakter default dan ukuran byte-buffer default adalah tepat.

Apakah ini berarti bahwa saya tidak diharuskan untuk mengatur pengkodean karakter sendiri jika saya menggunakan FileReader? Tapi saat ini saya mendapatkan data penyandian yang salah, apa cara yang benar untuk menangani situasi saya? Terima kasih.

nybon
sumber
Anda juga harus kehilangan String.valueOf () di dalam loop dan menggunakan StringBuffer.append (char [], int, int) secara langsung. Ini menghemat banyak penyalinan char []. Juga ganti StringBuffer dengan StringBuilder. Namun, semua ini bukan tentang pertanyaan Anda.
Joachim Sauer
1
Saya benci mengatakannya, tetapi apakah Anda sudah membaca JavaDoc tepat setelah bagian yang Anda tempelkan? Anda tahu, bagian yang mengatakan "Untuk menentukan sendiri nilai-nilai ini, buat InputStreamReader pada FileInputStream."?
Powerlord
Terima kasih atas komentar Anda, sebenarnya saya membaca JavaDoc, tetapi yang saya tidak yakin adalah apakah saya harus menentukan nilai-nilai ini sendiri, dan beralih ke "membangun InputStreamReader pada FileInputStream".
nybon
Ya, jika Anda tahu file tersebut berada dalam sesuatu selain dari pengkodean default platform, Anda harus memberi tahu InputStreamReader mana yang harus digunakan.
Alan Moore

Jawaban:

248

Ya, Anda perlu menentukan penyandian file yang ingin Anda baca.

Ya, ini artinya Anda harus mengetahui penyandian file yang ingin Anda baca.

Tidak, tidak ada cara umum untuk menebak penyandian file "teks biasa" apa pun yang diberikan.

Konstruktor satu argumenFileReader selalu menggunakan platform default encoding yang umumnya merupakan ide yang buruk .

Karena Java 11 FileReaderjuga mendapatkan konstruktor yang menerima pengkodean: new FileReader(file, charset)dan new FileReader(fileName, charset).

Di versi java sebelumnya, Anda harus menggunakan .new InputStreamReader(new FileInputStream(pathToFile), <encoding>)

Joachim Sauer
sumber
1
InputStream adalah = FileInputStream (nama file) baru; di sini saya mendapat file kesalahan tidak ditemukan kesalahan dengan nama file Rusia
Bhanu Sharma
3
+1 untuk saran penggunaan InputStreamReader, namun menggunakan tautan dalam blok kode membuatnya sulit untuk menyalin dan menempelkan kode, jika ini dapat diubah, thx
Ferrybig
1
Apakah itu "UTF-8" atau "UTF8" dalam penyandian. Menurut referensi Java SE pada encoding , karena InputStreamReaderapakah java.iokelas, itu akan menjadi "UTF8"?
NobleUplift
9
@NobleUplift: taruhan teraman adalah StandardCharsets.UTF_8, tidak ada kemungkinan salah ketik di sana ;-) Tapi ya, jika Anda menggunakan string "UTF8"akan benar (walaupun saya ingat bahwa itu akan menerima dua arah).
Joachim Sauer
1
@ JoachimSauer Sebenarnya, ini adalah salah satu tujuan dari Byte Order Mark, bersama dengan .. yah .. menetapkan urutan byte! :) Karena itu saya merasa aneh bahwa FileReader Java tidak dapat secara otomatis mendeteksi UTF-16 yang memiliki BOM seperti itu ... Bahkan saya pernah menulis a UnicodeFileReaderyang melakukan hal itu. Sayangnya sumber tertutup, tetapi Google memilikinya UnicodeReader yang sangat mirip.
Stijn de Witt
79

FileReader menggunakan penyandian default platform Java, yang tergantung pada pengaturan sistem komputer yang digunakannya dan umumnya penyandian paling populer di antara pengguna di lokal itu.

Jika "tebakan terbaik" ini tidak benar, maka Anda harus menentukan pengkodean secara eksplisit. Sayangnya, FileReadertidak mengizinkan ini (pengawasan besar dalam API). Sebagai gantinya, Anda harus menggunakan new InputStreamReader(new FileInputStream(filePath), encoding)dan idealnya mendapatkan encoding dari metadata tentang file tersebut.

Michael Borgwardt
sumber
24
"pengawasan besar di API" - terima kasih atas penjelasan ini - Saya bertanya-tanya mengapa saya tidak dapat menemukan konstruktor yang saya cari! Cheers John
monojohnny
@ Bhanu Sharma: itu masalah penyandian di tingkat yang berbeda, periksa dari mana Anda mendapatkan nama file, dan jika hardcoded menggunakan encoding apa yang digunakan kompilator.
Michael Borgwardt
1
@BhanuSharma: masalah pengodean nama file tidak ada hubungannya dengan pertanyaan ini. Lihat salah satu dari banyak pertanyaan "mengapa nama file Unicode tidak berfungsi di Jawa". Spoiler: java.io API seperti FileReader menggunakan panggilan sistem file library C standar, yang tidak dapat mendukung Unicode di Windows; pertimbangkan untuk menggunakan java.nio sebagai gantinya.
bobince
1
" FileReaderMenggunakan penyandian default platform Java, yang tergantung pada pengaturan sistem komputer yang digunakannya dan umumnya penyandian paling populer di antara pengguna di lokal itu." Saya tidak akan mengatakan itu. Setidaknya dari Windows. Untuk beberapa alasan teknis / historis yang aneh, JVM mengabaikan fakta bahwa Unicode adalah pengkodean yang direkomendasikan pada Windows untuk 'semua aplikasi baru' dan sebaliknya selalu bertindak seolah-olah pengkodean lawas yang dikonfigurasikan sebagai cadangan untuk aplikasi lawas adalah 'platform default'.
Stijn de Witt
6
Saya bahkan akan mengatakan bahwa jika aplikasi Java Anda tidak secara eksplisit menentukan pengkodean setiap kali itu membaca atau menulis ke file / stream / sumber daya, itu rusak , karena itu tidak dapat bekerja dengan andal saat itu.
Stijn de Witt
8

Karena Java 11 Anda dapat menggunakannya:

public FileReader(String fileName, Charset charset) throws IOException;
Radoslav Ivanov
sumber
6

Untuk Java 7+ doc Anda dapat menggunakan ini:

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

Ini semua Charset doc

Misalnya jika file Anda ada di CP1252, gunakan metode ini

Charset.forName("windows-1252");

Berikut adalah nama kanonik lainnya untuk penyandian Java baik untuk IO dan NIO doc

Jika Anda tidak tahu dengan tepat pengkodean Anda sudah mendapat dalam file, Anda dapat menggunakan beberapa libs pihak ketiga seperti alat ini dari Google ini yang bekerja cukup rapi.

Andreas Gelever
sumber
1

FileInputStream dengan InputStreamReader lebih baik daripada langsung menggunakan FileReader, karena yang terakhir tidak memungkinkan Anda untuk menentukan pengkodean charset.

Berikut adalah contoh menggunakan BufferedReader, FileInputStream dan InputStreamReader bersama-sama, sehingga Anda bisa membaca baris dari file.

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();
}
Guangtong Shen
sumber
0

Untuk yang lain sebagai bahasa Latin misalnya Cyrillic, Anda dapat menggunakan sesuatu seperti ini:

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

dan pastikan bahwa .txtfile Anda disimpan dengan format UTF-8(tetapi bukan sebagai default ANSI). Bersulang!

Iefimenko Ievgwn
sumber