All-inclusive-Zeichensatz zur Vermeidung von "java.nio.charset.MalformedInputException: Eingabelänge = 1"?

91

Ich erstelle ein einfaches Wordcount-Programm in Java, das die textbasierten Dateien eines Verzeichnisses liest.

Ich erhalte jedoch immer wieder den Fehler:

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

aus dieser Codezeile:

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

Ich weiß, dass ich das wahrscheinlich bekomme, weil ich ein verwendet habe Charset, das einige der Zeichen in den Textdateien nicht enthielt, von denen einige Zeichen anderer Sprachen enthielten. Aber ich möchte diese Zeichen einschließen.

Ich habe später bei den JavaDocs erfahren, dass das Charsetoptional ist und nur zum effizienteren Lesen der Dateien verwendet wird. Deshalb habe ich den Code geändert in:

BufferedReader reader = Files.newBufferedReader(file);

Aber einige Dateien werfen immer noch die MalformedInputException. Ich weiß nicht warum.

Ich habe mich gefragt, ob es ein All-Inclusive-Angebot gibt Charset, mit dem ich Textdateien mit vielen verschiedenen Zeichentypen lesen kann .

Vielen Dank.

Jonathan Lam
quelle

Antworten:

78

Sie möchten wahrscheinlich eine Liste der unterstützten Codierungen haben. Versuchen Sie für jede Datei nacheinander jede Codierung, möglicherweise beginnend mit UTF-8. MalformedInputExceptionVersuchen Sie jedes Mal, wenn Sie die fangen , die nächste Codierung.

Dawood ibn Kareem
quelle
41
Ich habe es versucht ISO-8859-1und es funktioniert gut. Ich denke, es ist für europäische Charaktere, was in Ordnung ist. Ich weiß immer noch nicht, warum UTF-16es nicht funktioniert.
Jonathan Lam
1
Wenn Sie Notepad ++ haben, können Sie versuchen, eine Textdatei zu öffnen, die Ihnen die Codierung der Datei im Menü anzeigt. Sie können den Code dann entsprechend anpassen, wenn Sie immer Dateien aus derselben Quelle erhalten.
JGFMK
@ JonathanLam Nun, denn wenn es mit codiert ist ISO-8859-1, dann ist es nicht UTF-16 . Diese Kodierungen sind völlig unterschiedlich. Eine Datei kann nicht beides sein.
Dawood ibn Kareem
@DawoodsaysreinstateMonica Ich glaube, ich meinte, ich war überrascht, dass UTF-16 nicht so gut funktioniert hat wie ein Allheilmittel für europäische Charaktere wie ISO-8859-1. Aber danke für die Info (auch wenn sechs Jahre später): P
Jonathan Lam
Sicher. UTF-16 enthält alle europäischen Zeichen. Sie sind jedoch anders dargestellt als ISO-8859-1. In ISO-8859-1 werden alle Zeichen mit nur 8 Bit dargestellt, sodass Sie auf 256 mögliche Zeichen beschränkt sind. In UTF-16 werden die meisten Zeichen mit 16 Bit und einige Zeichen mit 32 Bit dargestellt. Es gibt also viel mehr mögliche Zeichen in UTF-16, aber eine ISO-8859-1-Datei benötigt nur halb so viel Speicherplatz wie die gleichen Daten in UTF-16.
Dawood ibn Kareem
38

Erstellen von BufferedReader aus Files.newBufferedReader

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

Beim Ausführen der Anwendung kann die folgende Ausnahme ausgelöst werden:

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

Aber

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

funktioniert gut.

Der Unterschied besteht darin, dass der erstere die Standardaktion CharsetDecoder verwendet.

Die Standardaktion für fehlerhafte Eingabe- und nicht zuordnungsfähige Zeichenfehler besteht darin , sie zu melden .

während letzterer die Aktion REPLACE verwendet.

cs.newDecoder().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE)
Xin Wang
quelle
26

ISO-8859-1 ist ein All-Inclusive-Zeichensatz in dem Sinne, dass garantiert keine MalformedInputException ausgelöst wird. Es ist also gut zum Debuggen, auch wenn Ihre Eingabe nicht in diesem Zeichensatz enthalten ist. So:-

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

Ich hatte einige doppelte Anführungszeichen / doppelte linke Anführungszeichen in meiner Eingabe, und sowohl US-ASCII als auch UTF-8 haben MalformedInputException darauf geworfen, aber ISO-8859-1 hat funktioniert.

Tim Cooper
quelle
6

Ich bin auch auf diese Ausnahme mit der Fehlermeldung gestoßen.

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)

und festgestellt, dass ein seltsamer Fehler auftritt, wenn versucht wird, zu verwenden

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

um einen String "orazg 54" zu schreiben, der von einem generischen Typ in einer Klasse umgewandelt wurde.

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

Dieser String hat die Länge 9 und enthält Zeichen mit den folgenden Codepunkten:

111 114 97 122 103 9 53 52 10

Wenn jedoch der BufferedWriter in der Klasse durch Folgendes ersetzt wird:

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

Es kann diesen String ohne Ausnahmen erfolgreich schreiben. Wenn ich denselben String schreibe, der aus den Zeichen erstellt wurde, funktioniert er außerdem weiterhin in Ordnung.

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

Bisher bin ich bei der Verwendung des ersten BufferedWriter zum Schreiben von Strings nie auf eine Ausnahme gestoßen. Es ist ein seltsamer Fehler, der bei BufferedWriter auftritt, der aus java.nio.file.Files.newBufferedWriter (Pfad, Optionen) erstellt wurde.

Tom
quelle
1
Dies ist etwas unangebracht, da das OP eher über Lesen als über Schreiben sprach. Ich hatte ein ähnliches Problem aufgrund von BufferedWriter.write (int), das dieses int als Zeichen behandelt und es direkt in den Stream schreibt. Die Problemumgehung besteht darin, sie manuell in einen String zu konvertieren und dann zu schreiben.
Malaverdiere
Dies ist eine leider unterbewertete Antwort: Wirklich gute Arbeit, Tom. Ich frage mich, ob dies in späteren Versionen von Java behoben wurde.
Ryboflavin
5

ISO_8859_1 Hat für mich gearbeitet! Ich habe eine Textdatei mit durch Kommas getrennten Werten gelesen

Shahid Hussain Abbasi
quelle
3

Ich habe Folgendes geschrieben, um eine Liste der Ergebnisse zu drucken, die basierend auf den verfügbaren Zeichensätzen standardisiert werden sollen. Beachten Sie, dass Sie auch erfahren, welche Zeile von einer auf 0 basierenden Zeilennummer ausfällt, falls Sie Fehler beheben, welches Zeichen Probleme verursacht.

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
quelle
2

Versuchen Sie dies .. Ich hatte das gleiche Problem, unten Implementierung funktionierte für mich

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

Verwenden Sie dann Reader, wo immer Sie möchten.

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

Nun, das Problem ist, dass Files.newBufferedReader(Path path)es so implementiert wird:

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

Grundsätzlich macht es keinen Sinn, UTF-8etwas anzugeben, es sei denn, Sie möchten in Ihrem Code eine Beschreibung abgeben. Wenn Sie einen "breiteren" Zeichensatz ausprobieren möchten, können Sie es versuchen StandardCharsets.UTF_16, aber Sie können nicht 100% sicher sein, dass Sie trotzdem jeden möglichen Charakter erhalten.

francesco Foresti
quelle
0

UTF-8 funktioniert bei mir mit polnischen Zeichen

Adriano
quelle
-1

Sie können so etwas ausprobieren oder einfach kopieren und unter das Stück einfügen.

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
quelle
Der Ausnahmebehandler kann die while(exception)Schleife möglicherweise für immer erstellen, wenn er nie einen funktionierenden Zeichensatz im Array findet. Der Ausnahmebehandler sollte erneut ausgelöst werden, wenn das Ende des Arrays erreicht ist und kein funktionierender Zeichensatz gefunden wird. Außerdem hatte diese Antwort zum Zeitpunkt des Schreibens "-2" Stimmen. Ich habe es auf "-1" hochgestuft. Ich denke, der Grund für die negativen Stimmen ist, dass es keine ausreichende Erklärung gibt. Während ich verstehe, was der Code tut, können andere Leute nicht. Ein Kommentar wie "Sie können so etwas ausprobieren" wird von manchen Leuten möglicherweise nicht geschätzt.
mvanle