Charset รวมทั้งหมดเพื่อหลีกเลี่ยง“ java.nio.charset.MalformedInputException: Input length = 1”?


97

ฉันกำลังสร้างโปรแกรม wordcount อย่างง่ายใน Java ที่อ่านไฟล์ตามข้อความของไดเร็กทอรี

อย่างไรก็ตามฉันยังคงได้รับข้อผิดพลาด:

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

จากบรรทัดของรหัสนี้:

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

ฉันรู้ว่าฉันอาจได้รับสิ่งนี้เนื่องจากฉันใช้Charsetตัวอักษรที่ไม่มีอักขระบางตัวในไฟล์ข้อความซึ่งบางตัวมีอักขระของภาษาอื่นด้วย แต่ฉันต้องการรวมตัวละครเหล่านั้น

ฉันเรียนรู้ในภายหลังที่JavaDocsว่าCharsetเป็นทางเลือกและใช้เพื่อการอ่านไฟล์ที่มีประสิทธิภาพมากขึ้นเท่านั้นดังนั้นฉันจึงเปลี่ยนรหัสเป็น:

BufferedReader reader = Files.newBufferedReader(file);

แต่บางไฟล์ยังคงใช้ไฟล์MalformedInputException. ฉันไม่รู้ว่าทำไม

ฉันสงสัยว่ามีการรวมทุกอย่างCharsetที่จะช่วยให้ฉันอ่านไฟล์ข้อความที่มีอักขระประเภทต่างๆได้หรือไม่?

ขอบคุณ.

คำตอบ:


82

คุณอาจต้องการมีรายการการเข้ารหัสที่รองรับ สำหรับแต่ละไฟล์ให้ลองเข้ารหัสแต่ละครั้งโดยอาจเริ่มต้นด้วย UTF-8 ทุกครั้งที่จับได้ให้MalformedInputExceptionลองเข้ารหัสครั้งถัดไป


45
ฉันพยายามISO-8859-1แล้วและได้ผลดี ฉันคิดว่ามันเป็นสำหรับตัวละครในยุโรปซึ่งก็ใช้ได้ ฉันยังไม่รู้ว่าทำไมถึงUTF-16ไม่ได้ผล
Jonathan Lam

1
หากคุณมี Notepad ++ คุณสามารถลองเปิดไฟล์ข้อความและมันจะบอกการเข้ารหัสไฟล์ในเมนู จากนั้นคุณสามารถปรับเปลี่ยนโค้ดได้หากคุณได้รับไฟล์จากแหล่งเดียวกันเสมอ
JGFMK

@JonathanLam ดีเพราะถ้ามันเข้ารหัสด้วยISO-8859-1แล้วก็ไม่ได้ UTF-16การเข้ารหัสเหล่านี้แตกต่างกันอย่างสิ้นเชิง ไฟล์ไม่สามารถเป็นทั้งสองอย่าง
Dawood ibn Kareem

@DawoodsaysreinstateMonica ฉันเชื่อว่าฉันหมายความว่าฉันรู้สึกประหลาดใจ UTF-16 ไม่ทำงานและการจับทั้งหมดสำหรับตัวอักษรยุโรปเช่น ISO-8859-1 ดูเหมือนจะทำ แต่ขอบคุณสำหรับข้อมูล (แม้ว่าจะหกปีให้หลัง): P
Jonathan Lam

แน่นอน UTF-16 มีอักขระยุโรปทั้งหมดอยู่ในนั้น แต่แสดงต่างจาก ISO-8859-1 ใน ISO-8859-1 อักขระทั้งหมดจะแสดงด้วยเพียง 8 บิตดังนั้นคุณจึง จำกัด อักขระที่เป็นไปได้ 256 อักขระ ใน UTF-16 อักขระส่วนใหญ่จะแสดงด้วย 16 บิตและอักขระบางตัวจะแสดงด้วย 32 บิต ดังนั้นจึงมีอักขระที่เป็นไปได้มากขึ้นใน UTF-16 แต่ไฟล์ ISO-8859-1 จะใช้พื้นที่เพียงครึ่งเดียวเท่าที่ข้อมูลเดียวกันจะใช้ใน UTF-16
Dawood ibn Kareem

41

การสร้าง BufferedReader จาก Files.newBufferedReader

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

เมื่อเรียกใช้แอปพลิเคชันอาจทำให้เกิดข้อยกเว้นต่อไปนี้:

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

แต่

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

ทำได้ดี.

สิ่งที่แตกต่างคือในอดีตใช้การกระทำเริ่มต้น CharsetDecoder

การกระทำที่ไม่ถูกต้องเริ่มต้นสำหรับอินพุตและ unmappable ตัวอักษรข้อผิดพลาดคือการรายงานให้พวกเขา

ในขณะที่หลังใช้การดำเนินการ REPLACE

cs.newDecoder().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE)

29

ISO-8859-1 เป็นชุดอักขระที่รวมทุกอย่างในแง่ที่รับประกันว่าจะไม่โยน MalformedInputException ดังนั้นจึงเป็นการดีสำหรับการดีบักแม้ว่าอินพุตของคุณจะไม่อยู่ในชุดอักขระนี้ก็ตาม ดังนั้น:-

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

ฉันมีอักขระเครื่องหมายอัญประกาศคู่ขวา / คู่ซ้ายในข้อมูลที่ฉันป้อนและทั้ง US-ASCII และ UTF-8 โยน MalformedInputException ไว้ แต่ ISO-8859-1 ใช้งานได้


7

ฉันยังพบข้อยกเว้นนี้พร้อมข้อความแสดงข้อผิดพลาด

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)

และพบว่ามีข้อผิดพลาดแปลก ๆ เกิดขึ้นเมื่อพยายามใช้

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

เพื่อเขียน String "orazg 54" แคสต์จากประเภททั่วไปในคลาส

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

สตริงนี้มีความยาว 9 ประกอบด้วยอักขระที่มีจุดรหัสต่อไปนี้:

111 114 97 122103 9 53 52 10

อย่างไรก็ตามหาก BufferedWriter ในคลาสถูกแทนที่ด้วย:

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

สามารถเขียนสตริงนี้ได้สำเร็จโดยไม่มีข้อยกเว้น นอกจากนี้ถ้าฉันเขียน String เดียวกัน create จากตัวละครก็ยังใช้ได้

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

ก่อนหน้านี้ฉันไม่เคยพบ Exception ใด ๆ เมื่อใช้ BufferedWriter ตัวแรกเพื่อเขียน Strings ใด ๆ เป็นบั๊กแปลก ๆ ที่เกิดขึ้นกับ BufferedWriter ที่สร้างจาก java.nio.file.Files.newBufferedWriter (เส้นทางตัวเลือก)


1
สิ่งนี้ค่อนข้างไม่ตรงประเด็นเนื่องจาก OP กำลังพูดถึงการอ่านมากกว่าการเขียน ฉันมีปัญหาที่คล้ายกันเนื่องจาก BufferedWriter.write (int) - ซึ่งถือว่า int นั้นเป็นอักขระและเขียนลงในสตรีมโดยตรง วิธีแก้ปัญหาคือการแปลงเป็นสตริงด้วยตนเองแล้วจึงเขียน
malaverdiere

นี่เป็นคำตอบที่ได้รับการโหวตอย่างน่าเศร้าทอมทำงานได้ดีจริงๆ ฉันสงสัยว่าสิ่งนี้ได้รับการแก้ไขแล้วใน Java เวอร์ชันใหม่ ๆ หรือไม่
Ryboflavin


4

ลองดูสิ .. ฉันมีปัญหาเดียวกันด้านล่างการใช้งานได้ผลสำหรับฉัน

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

จากนั้นใช้ Reader ทุกที่ที่คุณต้องการ

foreg:

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

3

ฉันเขียนสิ่งต่อไปนี้เพื่อพิมพ์รายการผลลัพธ์เป็นมาตรฐานตามชุดอักขระที่มีอยู่ โปรดทราบว่ายังบอกด้วยว่าบรรทัดใดล้มเหลวจากหมายเลขบรรทัดตาม 0 ในกรณีที่คุณกำลังแก้ไขปัญหาอักขระใดที่ทำให้เกิดปัญหา

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

0

ปัญหาFiles.newBufferedReader(Path path)คือการดำเนินการดังนี้:

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

ดังนั้นโดยพื้นฐานแล้วไม่มีประเด็นใดในการระบุUTF-8เว้นแต่คุณจะต้องการอธิบายในโค้ดของคุณ หากคุณต้องการลองใช้ชุดอักขระที่ "กว้างขึ้น" คุณสามารถลองใช้ได้StandardCharsets.UTF_16แต่คุณไม่สามารถมั่นใจได้ 100% ว่าจะได้อักขระที่เป็นไปได้


-1

คุณสามารถลองสิ่งนี้หรือเพียงแค่คัดลอกและวางชิ้นส่วนด้านล่าง

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 ++;
    }
}

ตัวจัดการข้อยกเว้นสามารถสร้างwhile(exception)ลูปได้ตลอดไปหากไม่พบชุดอักขระที่ใช้งานได้ในอาร์เรย์ ตัวจัดการข้อยกเว้นควรเพิ่มใหม่หากถึงจุดสิ้นสุดของอาร์เรย์และไม่พบชุดอักขระที่ใช้งานได้ นอกจากนี้ในขณะที่เขียนคำตอบนี้มีคะแนนโหวต "-2" ฉันได้เพิ่มคะแนนให้เป็น "-1" ฉันคิดว่าสาเหตุที่ได้รับคะแนนโหวตเป็นลบเนื่องจากไม่มีคำอธิบายที่เพียงพอ แม้ว่าฉันจะเข้าใจว่าโค้ดทำอะไร แต่คนอื่นอาจไม่เข้าใจ ดังนั้นความคิดเห็นเช่น "ลองทำแบบนี้ก็ได้" อาจไม่ถูกใจบางคน
mvanle

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.