"java.nio.charset.MalformedInputException : Input length = 1"을 피하기위한 모든 포함 문자셋?


96

디렉토리의 텍스트 기반 파일을 읽는 Java로 간단한 wordcount 프로그램을 만들고 있습니다.

그러나 계속 오류가 발생합니다.

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

이 코드 줄에서 :

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

Charset텍스트 파일에 일부 문자가 포함되지 않은 a 를 사용했기 때문에이 메시지를 얻을 수 있으며 일부는 다른 언어의 문자를 포함합니다. 하지만 그 캐릭터를 포함하고 싶습니다.

나중에 JavaDocs 에서는 Charset선택 사항이며 파일을보다 효율적으로 읽는 데만 사용 된다는 사실을 알게 되었으므로 코드를 다음과 같이 변경했습니다.

BufferedReader reader = Files.newBufferedReader(file);

그러나 일부 파일은 여전히 MalformedInputException. 이유를 모르겠습니다.

Charset다양한 유형의 문자가있는 텍스트 파일을 읽을 수 있는 모든 기능이 있는지 궁금합니다 .

감사.

답변:


81

지원되는 인코딩 목록이 필요할 수 있습니다. 각 파일에 대해 UTF-8로 시작하여 각 인코딩을 차례로 시도하십시오. 를 잡을 때마다 MalformedInputException다음 인코딩을 시도하십시오.


44
나는 시도 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과 같은 유럽 문자에 대한 포괄적 인 기능만큼 잘 작동하지 않는다는 것에 놀랐습니다. 하지만 정보 감사합니다 (6 년 후라도) : 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

40

Files.newBufferedReader에서 BufferedReader 만들기

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 기본 작업을 사용한다는 것입니다.

잘못된 입력 및 매핑 할 수없는 문자 오류에 대한 기본 조치는이를 보고 하는 것입니다.

후자는 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이 작동했습니다.


6

또한 오류 메시지와 함께이 예외가 발생했습니다.

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

클래스의 일반 유형에서 캐스트 된 문자열 "orazg 54"를 작성합니다.

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

이 문자열은 다음 코드 포인트가있는 문자를 포함하는 길이 9입니다.

111114 97122103 9 53 52 10

그러나 클래스의 BufferedWriter가 다음으로 대체되는 경우 :

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

예외없이이 문자열을 성공적으로 쓸 수 있습니다. 또한 문자에서 동일한 문자열을 작성하면 여전히 정상적으로 작동합니다.

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

이전에는 첫 번째 BufferedWriter를 사용하여 문자열을 쓸 때 예외가 발생하지 않았습니다. java.nio.file.Files.newBufferedWriter (path, options)에서 생성 된 BufferedWriter에 발생하는 이상한 버그입니다.


1
OP가 쓰기보다는 읽기에 대해 이야기했기 때문에 이것은 다소 주제에서 벗어난 것입니다. BufferedWriter.write (int)로 인해 비슷한 문제가 발생했습니다. 이는 해당 int를 문자로 처리하고 스트림에 직접 씁니다. 해결 방법은 수동으로 문자열로 변환 한 다음 쓰는 것입니다.
malaverdiere

이것은 슬프게도 투표에 응한 답변입니다. 정말 좋은 일 Tom. 이 문제가 이후 버전의 Java에서 해결되었는지 궁금합니다.
Ryboflavin


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

3

이것을 시도하십시오 .. 나는 동일한 문제가 있었는데, 아래 구현이 나를 위해 일했습니다.

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

그런 다음 원하는 곳에서 Reader를 사용하십시오.

예 :

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

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.