Kompletny zestaw znaków, aby uniknąć „java.nio.charset.MalformedInputException: Input length = 1”?


97

Tworzę prosty program do liczenia słów w Javie, który czyta pliki tekstowe katalogu.

Jednak ciągle otrzymuję błąd:

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

z tego wiersza kodu:

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

Wiem, że prawdopodobnie to rozumiem, ponieważ użyłem Charset znaku, który nie zawierał niektórych znaków w plikach tekstowych, z których niektóre zawierały znaki innych języków. Ale chcę uwzględnić te postacie.

Później dowiedziałem się w JavaDocs, że Charsetjest opcjonalny i używany tylko do bardziej wydajnego odczytu plików, więc zmieniłem kod na:

BufferedReader reader = Files.newBufferedReader(file);

Ale niektóre pliki nadal wyrzucają MalformedInputException. Nie wiem dlaczego.

Zastanawiałem się, czy istnieje all-inclusive Charset, który pozwoli mi czytać pliki tekstowe z wieloma różnymi typami znaków ?

Dzięki.

Odpowiedzi:


82

Prawdopodobnie chcesz mieć listę obsługiwanych kodowań. Dla każdego pliku wypróbuj każde kodowanie po kolei, być może zaczynając od UTF-8. Za każdym razem, gdy złapiesz MalformedInputException, spróbuj następnego kodowania.


45
Próbowałem ISO-8859-1i działa dobrze. Myślę, że to dla europejskich postaci, co jest w porządku. Nadal nie wiem, dlaczego UTF-16nie działa.
Jonathan Lam,

1
Jeśli masz Notepad ++, możesz spróbować otworzyć plik tekstowy, który powie Ci kodowanie pliku w Menu. Możesz wtedy dostosować kod w sposób dowolny, jeśli zawsze otrzymujesz plik z tego samego źródła.
JGFMK

@JonathanLam Cóż, ponieważ jeśli jest zakodowany ISO-8859-1, to nie jest UTF-16 . Te kodowania są zupełnie inne. Plik nie może być jednym i drugim.
Dawood ibn Kareem

@DawoodsaysreinstateMonica Myślę, że chodziło mi o to, że byłem zaskoczony, iż UTF-16 nie działał tak dobrze, jak wydaje się, że działa tak dobrze dla znaków europejskich, takich jak ISO-8859-1. Ale dzięki za informacje (nawet jeśli sześć lat później): P
Jonathan Lam

Pewnie. UTF-16 zawiera wszystkie europejskie znaki. Ale są reprezentowane inaczej niż ISO-8859-1. W ISO-8859-1 wszystkie znaki są reprezentowane za pomocą tylko 8 bitów, więc masz ograniczenie do 256 możliwych znaków. W UTF-16 większość znaków jest reprezentowana za pomocą 16 bitów, a niektóre znaki są reprezentowane za pomocą 32 bitów. Tak więc w UTF-16 jest o wiele więcej możliwych znaków, ale plik ISO-8859-1 będzie wymagał tylko o połowę mniej miejsca niż te same dane, które wykorzystywałyby w UTF-16.
Dawood ibn Kareem

41

Tworzenie BufferedReader z Files.newBufferedReader

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

podczas uruchamiania aplikacji może zgłosić następujący wyjątek:

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

Ale

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

działa dobrze.

Różnica polega na tym, że ta pierwsza używa domyślnej akcji CharsetDecoder.

Domyślnym działaniem w przypadku błędnych danych wejściowych i błędów związanych ze znakami niemapowalnymi jest ich zgłoszenie .

podczas gdy ta ostatnia używa akcji REPLACE.

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

29

ISO-8859-1 to kompletny zestaw znaków, w tym sensie, że gwarantuje, że nie zgłosi wyjątku MalformedInputException. Jest więc dobry do debugowania, nawet jeśli twoje dane wejściowe nie są w tym zestawie znaków. Więc:-

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

Miałem kilka znaków podwójnego cudzysłowu z prawej strony / podwójnego cudzysłowu z lewej strony w danych wejściowych i zarówno US-ASCII, jak i UTF-8 rzuciły na nie MalformedInputException, ale ISO-8859-1 działało.


7

Napotkałem również ten wyjątek z komunikatem o błędzie,

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)

i odkryłem, że podczas próby użycia pojawia się jakiś dziwny błąd

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

napisać rzutowanie typu String "orazg 54" z typu ogólnego w klasie.

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

Ten łańcuch ma długość 9 i zawiera znaki z następującymi punktami kodowymi:

111 114 97 122 103 9 53 52 10

Jeśli jednak BufferedWriter w klasie zostanie zastąpiony przez:

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

może z powodzeniem napisać ten ciąg bez wyjątków. Ponadto, jeśli napiszę ten sam ciąg utworzony ze znaków, nadal działa 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();

Wcześniej nigdy nie napotkałem żadnego wyjątku podczas używania pierwszego BufferedWriter do pisania jakichkolwiek ciągów. To dziwny błąd, który występuje w BufferedWriter utworzonym z java.nio.file.Files.newBufferedWriter (ścieżka, opcje)


1
To trochę nie na temat, ponieważ PO mówił o czytaniu, a nie pisaniu. Miałem podobny problem z powodu BufferedWriter.write (int) - który traktuje ten int jako znak i zapisuje go bezpośrednio do strumienia. Sposób obejścia problemu polega na ręcznym przekonwertowaniu go na ciąg, a następnie zapisaniu.
malaverdiere

To jest niestety niedotrzymana odpowiedź. Naprawdę fajna robota Tom. Zastanawiam się, czy problem ten został rozwiązany w późniejszych wersjach Javy.
Ryboflavin


4

spróbuj tego .. miałem ten sam problem, poniżej implementacja działała dla mnie

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

następnie używaj programu Reader, gdziekolwiek chcesz.

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

Napisałem poniżej, aby wydrukować listę wyników do standaryzacji w oparciu o dostępne zestawy znaków. Zwróć uwagę, że informuje również o tym, która linia nie działa z numeru linii opartego na 0, w przypadku rozwiązywania problemów ze znakiem powodującym problemy.

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

Cóż, problem polega na tym, że Files.newBufferedReader(Path path)jest to realizowane w następujący sposób:

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

więc w zasadzie nie ma sensu określać, UTF-8chyba że chcesz, aby kod był opisowy. Jeśli chcesz wypróbować „szerszy” zestaw znaków, możesz spróbować StandardCharsets.UTF_16, ale i tak nie możesz być w 100% pewien, że otrzymasz wszystkie możliwe znaki.


-1

możesz spróbować czegoś takiego lub po prostu skopiować i wkleić poniższy fragment.

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

Program obsługi wyjątków może potencjalnie uczynić while(exception)pętlę wieczną, jeśli nigdy nie znajdzie działającego zestawu znaków w tablicy. Program obsługi wyjątków powinien ponownie zgłosić, jeśli osiągnięty zostanie koniec tablicy i nie zostanie znaleziony działający zestaw znaków. Ponadto w chwili pisania tej odpowiedzi liczba głosów wynosiła „-2”. Głosowałem za „-1”. Myślę, że powodem, dla którego otrzymała głosy negatywne, jest niewystarczające wyjaśnienie. Chociaż rozumiem, co robi kod, inni mogą nie. Dlatego komentarz typu „możesz spróbować czegoś takiego” może nie zostać doceniony przez niektórych ludzi.
mvanle

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.