Tất cả Charset bao gồm để tránh “java.nio.charset.MalformedInputException: Input length = 1”?


97

Tôi đang tạo một chương trình wordcount đơn giản trong Java để đọc qua các tệp dựa trên văn bản của thư mục.

Tuy nhiên, tôi tiếp tục nhận được lỗi:

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

từ dòng mã này:

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

Tôi biết tôi có thể nhận được điều này vì tôi đã sử dụng một Charsetkhông bao gồm một số ký tự trong tệp văn bản, một số trong đó bao gồm các ký tự của các ngôn ngữ khác. Nhưng tôi muốn đưa những nhân vật đó vào.

Sau đó, tôi đã học được tại JavaDocs rằng mã Charsetnày là tùy chọn và chỉ được sử dụng để đọc tệp hiệu quả hơn, vì vậy tôi đã thay đổi mã thành:

BufferedReader reader = Files.newBufferedReader(file);

Nhưng một số tệp vẫn ném MalformedInputException. Tôi không biết tại sao.

Tôi đã tự hỏi liệu có tính năng bao gồm tất cả Charsetsẽ cho phép tôi đọc các tệp văn bản với nhiều loại ký tự khác nhau không?

Cảm ơn.

Câu trả lời:


81

Bạn có thể muốn có một danh sách các bảng mã được hỗ trợ. Đối với mỗi tệp, hãy thử lần lượt từng mã hóa, có thể bắt đầu bằng UTF-8. Mỗi khi bạn bắt được MalformedInputException, hãy thử mã hóa tiếp theo.


44
Tôi đã thử ISO-8859-1và nó hoạt động tốt. Tôi nghĩ nó dành cho các nhân vật châu Âu, điều đó ổn. Tôi vẫn không biết tại sao UTF-16nó không hoạt động.
Jonathan Lam

1
Nếu bạn có Notepad ++, bạn có thể thử mở tệp văn bản và nó sẽ cho bạn biết mã hóa của tệp trong Menu. Sau đó, bạn có thể điều chỉnh mã một cách nhanh chóng nếu bạn luôn nhận được tệp từ cùng một nguồn.
JGFMK

@JonathanLam Chà, bởi vì nếu nó được mã hóa bằng ISO-8859-1, thì nó không phải UTF-16 . Các bảng mã này hoàn toàn khác nhau. Một tệp không thể là cả hai.
Dawood ibn Kareem

@DawoodsaysreinstateMonica. Nhưng cảm ơn vì thông tin (ngay cả khi sáu năm sau): P
Jonathan Lam

Chắc chắn rồi. UTF-16 có tất cả các ký tự Châu Âu trong đó. Nhưng chúng được thể hiện khác với ISO-8859-1. Trong ISO-8859-1, tất cả các ký tự chỉ được biểu diễn bằng 8 bit, vì vậy bạn bị giới hạn ở 256 ký tự có thể. Trong UTF-16, hầu hết các ký tự được biểu diễn bằng 16 bit và một số ký tự được biểu diễn bằng 32 bit. Vì vậy, có thể có nhiều ký tự hơn trong UTF-16, nhưng tệp ISO-8859-1 sẽ chỉ yêu cầu một nửa dung lượng so với cùng một dữ liệu sẽ sử dụng trong UTF-16.
Dawood ibn Kareem

40

Tạo BufferedReader từ Files.newBufferedReader

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

khi chạy ứng dụng, nó có thể đưa ra ngoại lệ sau:

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

Nhưng

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

hoạt động tốt.

Sự khác biệt là, trước đây sử dụng hành động mặc định CharsetDecoder.

Hành động mặc định đối với lỗi ký tự đầu vào không đúng định dạng và không thể điều khiển là báo cáo chúng.

trong khi cái sau sử dụng hành động REPLACE.

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

29

ISO-8859-1 là một bộ ký tự bao gồm tất cả, theo nghĩa nó được đảm bảo không tạo ra MalformedInputException. Vì vậy, nó rất tốt cho việc gỡ lỗi, ngay cả khi đầu vào của bạn không có trong bộ ký tự này. Vì thế:-

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

Tôi có một số ký tự ngoặc kép-phải / ngoặc kép-trái trong đầu vào của mình và cả US-ASCII và UTF-8 đều có MalformedInputException trên chúng, nhưng ISO-8859-1 vẫn hoạt động.


7

Tôi cũng gặp phải ngoại lệ này với thông báo lỗi,

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)

và nhận thấy rằng một số lỗi lạ xảy ra khi cố gắng sử dụng

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

để viết Chuỗi "orazg 54" được ép kiểu từ một kiểu chung trong một lớp.

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

Chuỗi này có độ dài 9 chứa các ký tự với các điểm mã sau:

111 114 97 122 103 9 53 52 10

Tuy nhiên, nếu BufferedWriter trong lớp được thay thế bằng:

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

nó có thể ghi thành công Chuỗi này mà không có ngoại lệ. Ngoài ra, nếu tôi viết cùng một Chuỗi tạo từ các ký tự thì nó vẫn hoạt động OK.

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

Trước đây tôi chưa bao giờ gặp bất kỳ Ngoại lệ nào khi sử dụng BufferedWriter đầu tiên để viết bất kỳ Chuỗi nào. Đó là một lỗi lạ xảy ra với BufferedWriter được tạo từ java.nio.file.Files.newBufferedWriter (đường dẫn, tùy chọn)


1
Điều này hơi lạc đề, vì OP đang nói về việc đọc, thay vì viết. Tôi đã gặp sự cố tương tự do BufferedWriter.write (int) - xử lý int đó như một ký tự và ghi trực tiếp vào luồng. Cách giải quyết là chuyển đổi thủ công thành chuỗi và sau đó viết.
malaverdiere

Đây là một câu trả lời được bình chọn một cách đáng buồn. Tôi tự hỏi nếu điều này đã được giải quyết trong các phiên bản Java sau này.
Ryboflavin

5

ISO_8859_1 Phù hợp với tôi! Tôi đang đọc tệp văn bản với các giá trị được phân tách bằng dấu phẩy


4

hãy thử cái này .. tôi gặp vấn đề tương tự, cách triển khai bên dưới phù hợp với tôi

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

sau đó sử dụng Reader ở bất cứ đâu bạn muốn.

nói trước:

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

Tôi đã viết như sau để in danh sách kết quả ra tiêu chuẩn dựa trên các bảng mã có sẵn. Lưu ý rằng nó cũng cho bạn biết dòng nào bị lỗi từ số dòng dựa trên 0 trong trường hợp bạn đang khắc phục sự cố ký tự nào gây ra sự cố.

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

Chà, vấn đề là nó Files.newBufferedReader(Path path)được triển khai như thế này:

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

vì vậy về cơ bản không có ích gì UTF-8khi chỉ định trừ khi bạn muốn mô tả trong mã của mình. Nếu bạn muốn thử một bộ ký tự "rộng hơn", bạn có thể thử StandardCharsets.UTF_16, nhưng dù sao thì bạn cũng không thể chắc chắn 100% để có được mọi ký tự có thể.


-1

bạn có thể thử một cái gì đó như thế này, hoặc chỉ cần sao chép và dán đoạn bên dưới.

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

Trình xử lý ngoại lệ có thể tạo while(exception)vòng lặp mãi mãi nếu nó không bao giờ tìm thấy một bộ ký tự hoạt động trong mảng. Trình xử lý ngoại lệ sẽ được ném lại nếu đến cuối mảng và không tìm thấy bộ ký tự nào đang hoạt động. Ngoài ra, tại thời điểm viết câu trả lời này đã có "-2" phiếu bầu. Tôi đã ủng hộ nó thành "-1". Tôi nghĩ lý do nó nhận được phiếu tiêu cực là vì không có lời giải thích đầy đủ. Mặc dù tôi hiểu mã hoạt động nhưng những người khác có thể không. Vì vậy, một nhận xét như "bạn có thể thử một cái gì đó như thế này" có thể không được đánh giá cao bởi một số người.
mvanle

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.