Semua Charset inklusif untuk menghindari "java.nio.charset.MalformedInputException: Input length = 1"?

98

Saya membuat program wordcount sederhana di Java yang membaca file berbasis teks di direktori.

Namun, saya terus mendapatkan kesalahan:

java.nio.charset.MalformedInputException: Input length = 1

dari baris kode ini:

BufferedReader reader = Files.newBufferedReader(file,Charset.forName("UTF-8"));

Saya tahu saya mungkin mendapatkan ini karena saya menggunakan Charsetyang tidak menyertakan beberapa karakter dalam file teks, beberapa di antaranya menyertakan karakter bahasa lain. Tapi saya ingin memasukkan karakter itu.

Saya kemudian belajar di JavaDocs bahwa Charsetitu opsional dan hanya digunakan untuk pembacaan file yang lebih efisien, jadi saya mengubah kodenya menjadi:

BufferedReader reader = Files.newBufferedReader(file);

Tetapi beberapa file masih membuang file MalformedInputException. Saya tidak tahu kenapa.

Saya bertanya-tanya apakah ada all-inclusive Charsetyang memungkinkan saya membaca file teks dengan berbagai jenis karakter ?

Terima kasih.

Jonathan Lam
sumber

Jawaban:

82

Anda mungkin ingin memiliki daftar pengkodean yang didukung. Untuk setiap file, coba setiap encoding secara bergantian, mungkin dimulai dengan UTF-8. Setiap kali Anda menangkapnya MalformedInputException, coba encoding berikutnya.

Dawood ibn Kareem
sumber
45
Saya mencoba ISO-8859-1dan itu bekerja dengan baik. Saya pikir itu untuk karakter Eropa, itu bagus. Saya masih tidak tahu mengapa UTF-16tidak berhasil.
Jonathan Lam
1
Jika Anda memiliki Notepad ++, Anda dapat mencoba membuka file teks dan itu akan memberi tahu Anda pengkodean file di Menu. Anda kemudian dapat menyesuaikan kode dengan tepat jika Anda selalu mendapatkan file dari sumber yang sama.
JGFMK
@JonathanLam Yah, karena jika dikodekan dengan ISO-8859-1, maka tidak UTF-16 . Pengodean ini sangat berbeda. File tidak bisa menjadi keduanya.
Dawood ibn Kareem
@DawoodsaysreinstateMonica Saya yakin maksud saya, saya terkejut UTF-16 tidak berfungsi sebaik mungkin untuk karakter Eropa seperti ISO-8859-1. Tapi terima kasih atas infonya (bahkan jika enam tahun kemudian): P
Jonathan Lam
Tentu. UTF-16 memiliki semua karakter Eropa di dalamnya. Tapi mereka direpresentasikan secara berbeda dari ISO-8859-1. Dalam ISO-8859-1, semua karakter diwakili dengan hanya 8 bit, jadi Anda dibatasi hingga 256 karakter yang memungkinkan. Dalam UTF-16, sebagian besar karakter direpresentasikan dengan 16 bit, dan beberapa karakter direpresentasikan dengan 32 bit. Jadi ada lebih banyak kemungkinan karakter dalam UTF-16, tetapi file ISO-8859-1 hanya akan membutuhkan setengah dari ruang yang digunakan data yang sama dalam UTF-16.
Dawood ibn Kareem
41

Membuat BufferedReader dari Files.newBufferedReader

Files.newBufferedReader(Paths.get("a.txt"), StandardCharsets.UTF_8);

saat menjalankan aplikasi itu mungkin memunculkan pengecualian berikut:

java.nio.charset.MalformedInputException: Input length = 1

Tapi

new BufferedReader(new InputStreamReader(new FileInputStream("a.txt"),"utf-8"));

bekerja dengan baik.

Perbedaannya adalah, yang pertama menggunakan tindakan default CharsetDecoder.

Tindakan default untuk kesalahan input yang salah dan karakter yang tidak dapat diterapkan adalah melaporkannya .

sedangkan yang terakhir menggunakan aksi REPLACE.

cs.newDecoder().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE)
Xin Wang
sumber
29

ISO-8859-1 adalah rangkaian karakter lengkap, dalam arti dijamin tidak akan memunculkan MalformedInputException. Jadi bagus untuk debugging, bahkan jika input Anda tidak ada di charset ini. Begitu:-

req.setCharacterEncoding("ISO-8859-1");

Saya memiliki beberapa karakter double-right-quote / double-left-quote dalam masukan saya, dan US-ASCII dan UTF-8 melemparkan MalformedInputException pada mereka, tetapi ISO-8859-1 berfungsi.

Tim Cooper
sumber
7

Saya juga mengalami pengecualian ini dengan pesan kesalahan,

java.nio.charset.MalformedInputException: Input length = 1
at java.nio.charset.CoderResult.throwException(Unknown Source)
at sun.nio.cs.StreamEncoder.implWrite(Unknown Source)
at sun.nio.cs.StreamEncoder.write(Unknown Source)
at java.io.OutputStreamWriter.write(Unknown Source)
at java.io.BufferedWriter.flushBuffer(Unknown Source)
at java.io.BufferedWriter.write(Unknown Source)
at java.io.Writer.write(Unknown Source)

dan menemukan bahwa beberapa bug aneh terjadi saat mencoba menggunakan

BufferedWriter writer = Files.newBufferedWriter(Paths.get(filePath));

untuk menulis string "orazg 54" dari tipe generik di kelas.

//key is of generic type <Key extends Comparable<Key>>
writer.write(item.getKey() + "\t" + item.getValue() + "\n");

String ini memiliki panjang 9 yang berisi karakter dengan poin kode berikut:

111 114 97 122103 9 53 52 10

Namun, jika BufferedWriter di kelas diganti dengan:

FileOutputStream outputStream = new FileOutputStream(filePath);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));

itu bisa berhasil menulis String ini tanpa pengecualian. Selain itu, jika saya menulis String yang sama yang dibuat dari karakter itu masih berfungsi dengan baik.

String string = new String(new char[] {111, 114, 97, 122, 103, 9, 53, 52, 10});
BufferedWriter writer = Files.newBufferedWriter(Paths.get("a.txt"));
writer.write(string);
writer.close();

Sebelumnya saya tidak pernah menemukan Exception saat menggunakan BufferedWriter pertama untuk menulis Strings. Ini adalah bug aneh yang terjadi pada BufferedWriter yang dibuat dari java.nio.file.Files.newBufferedWriter (jalur, opsi)

Tom
sumber
1
Ini agak di luar topik, karena OP berbicara tentang membaca, daripada menulis. Saya memiliki masalah serupa karena BufferedWriter.write (int) - yang memperlakukan int itu sebagai karakter dan menulisnya langsung ke aliran. Solusinya adalah dengan mengonversinya secara manual menjadi string dan kemudian menulis.
malaverdiere
Ini adalah jawaban yang sayangnya tidak dipilih, Kerja yang sangat bagus Tom. Saya bertanya-tanya apakah ini telah diselesaikan di versi Java yang lebih baru.
Ryboflavin
5

ISO_8859_1 Bekerja untuk saya! Saya sedang membaca file teks dengan nilai yang dipisahkan koma

Shahid Hussain Abbasi
sumber
4

coba ini .. saya memiliki masalah yang sama, implementasi di bawah ini berhasil untuk saya

Reader reader = Files.newBufferedReader(Paths.get(<yourfilewithpath>), StandardCharsets.ISO_8859_1);

lalu gunakan Pustaka di mana pun Anda inginkan.

depan:

CsvToBean<anyPojo> csvToBean = null;
    try {
        Reader reader = Files.newBufferedReader(Paths.get(csvFilePath), 
                        StandardCharsets.ISO_8859_1);
        csvToBean = new CsvToBeanBuilder(reader)
                .withType(anyPojo.class)
                .withIgnoreLeadingWhiteSpace(true)
                .withSkipLines(1)
                .build();

    } catch (IOException e) {
        e.printStackTrace();
    }
Vin
sumber
3

Saya menulis berikut ini untuk mencetak daftar hasil ke standar berdasarkan charsets yang tersedia. Perhatikan bahwa ini juga memberi tahu Anda baris apa yang gagal dari nomor baris berbasis 0 jika Anda memecahkan masalah karakter apa yang menyebabkan masalah.

public static void testCharset(String fileName) {
    SortedMap<String, Charset> charsets = Charset.availableCharsets();
    for (String k : charsets.keySet()) {
        int line = 0;
        boolean success = true;
        try (BufferedReader b = Files.newBufferedReader(Paths.get(fileName),charsets.get(k))) {
            while (b.ready()) {
                b.readLine();
                line++;
            }
        } catch (IOException e) {
            success = false;
            System.out.println(k+" failed on line "+line);
        }
        if (success) 
            System.out.println("*************************  Successs "+k);
    }
}
EngineerWithJava54321
sumber
0

Nah, masalahnya adalah yang Files.newBufferedReader(Path path)diimplementasikan seperti ini:

public static BufferedReader newBufferedReader(Path path) throws IOException {
    return newBufferedReader(path, StandardCharsets.UTF_8);
}

jadi pada dasarnya tidak ada gunanya menentukan UTF-8kecuali Anda ingin deskriptif dalam kode Anda. Jika Anda ingin mencoba rangkaian karakter yang "lebih luas", Anda dapat mencobanya StandardCharsets.UTF_16, tetapi Anda tidak dapat 100% yakin untuk mendapatkan setiap karakter yang memungkinkan.

francesco foresti
sumber
-1

Anda dapat mencoba sesuatu seperti ini, atau cukup salin dan tempel bagian di bawah ini.

boolean exception = true;
Charset charset = Charset.defaultCharset(); //Try the default one first.        
int index = 0;

while(exception) {
    try {
        lines = Files.readAllLines(f.toPath(),charset);
          for (String line: lines) {
              line= line.trim();
              if(line.contains(keyword))
                  values.add(line);
              }           
        //No exception, just returns
        exception = false; 
    } catch (IOException e) {
        exception = true;
        //Try the next charset
        if(index<Charset.availableCharsets().values().size())
            charset = (Charset) Charset.availableCharsets().values().toArray()[index];
        index ++;
    }
}
Pengxiang
sumber
Penangan pengecualian berpotensi membuat while(exception)loop selamanya jika tidak pernah menemukan charset yang berfungsi dalam larik. Pengendali pengecualian harus memutar ulang jika akhir larik tercapai dan tidak ada rangkaian karakter yang berfungsi ditemukan. Juga, pada saat penulisan, jawaban ini memiliki suara "-2". Saya telah memilihnya menjadi "-1". Saya kira alasan mendapat suara negatif adalah karena penjelasan yang kurang memadai. Meskipun saya memahami fungsi kode tersebut, orang lain mungkin tidak. Jadi, komentar seperti "Anda bisa mencoba yang seperti ini" mungkin tidak disukai oleh sebagian orang.
mvanle
-1

UTF-8 bekerja untuk saya dengan karakter Polandia

Adriano
sumber