Set di caratteri tutto compreso per evitare "java.nio.charset.MalformedInputException: lunghezza di input = 1"?


97

Sto creando un semplice programma di conteggio parole in Java che legge i file di testo di una directory.

Tuttavia, continuo a ricevere l'errore:

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

da questa riga di codice:

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

So che probabilmente ho capito perché ho usato un Charsetche non includeva alcuni caratteri nei file di testo, alcuni dei quali includevano caratteri di altre lingue. Ma voglio includere quei personaggi.

Successivamente ho appreso al JavaDocs che Charsetè opzionale e utilizzato solo per una lettura più efficiente dei file, quindi ho cambiato il codice in:

BufferedReader reader = Files.newBufferedReader(file);

Ma alcuni file continuano a lanciare l'estensione MalformedInputException. Non so perché

Mi chiedevo se esiste un all-inclusive Charsetche mi permetterà di leggere file di testo con tanti tipi diversi di caratteri ?

Grazie.

Risposte:


82

Probabilmente vorrai avere un elenco di codifiche supportate. Per ogni file, prova a turno ciascuna codifica, magari iniziando con UTF-8. Ogni volta che prendi il MalformedInputException, prova la codifica successiva.


45
Ho provato ISO-8859-1e funziona bene. Penso che sia per i personaggi europei, il che va bene. Non so ancora perché UTF-16non funziona, però.
Jonathan Lam

1
Se hai Notepad ++, puoi provare ad aprire il file di testo e ti dirà la codifica del file nel Menu. È quindi possibile adattare il codice in modo appropriato se si ottiene sempre il file dalla stessa fonte.
JGFMK

@JonathanLam Bene, perché se è codificato con ISO-8859-1, allora non lo è UTF-16 . Queste codifiche sono completamente diverse. Un file non può essere entrambi.
Dawood ibn Kareem

@DawoodsaysreinstateMonica Credo volessi dire che ero sorpreso che UTF-16 non funzionasse così bene come sembra fare un tuttofare per i caratteri europei come ISO-8859-1. Ma grazie per le informazioni (anche se sei anni dopo): P
Jonathan Lam

Sicuro. UTF-16 contiene tutti i caratteri europei. Ma sono rappresentati in modo diverso da ISO-8859-1. In ISO-8859-1, tutti i caratteri sono rappresentati con solo 8 bit, quindi sei limitato a 256 caratteri possibili. In UTF-16, la maggior parte dei caratteri è rappresentata con 16 bit e alcuni caratteri sono rappresentati con 32 bit. Quindi ci sono molti più caratteri possibili in UTF-16, ma un file ISO-8859-1 richiederà solo la metà dello spazio che gli stessi dati utilizzerebbero in UTF-16.
Dawood ibn Kareem

41

Creazione di BufferedReader da Files.newBufferedReader

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

durante l'esecuzione dell'applicazione potrebbe generare la seguente eccezione:

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

Ma

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

funziona bene.

La differenza è che il primo utilizza l'azione predefinita CharsetDecoder.

L'azione predefinita per gli errori di input non valido e di caratteri non mappabili è segnalarli .

mentre quest'ultimo utilizza l'azione REPLACE.

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

29

ISO-8859-1 è un set di caratteri all-inclusive, nel senso che è garantito che non generi MalformedInputException. Quindi è utile per il debug, anche se l'input non è in questo set di caratteri. Così:-

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

Avevo alcuni caratteri di virgolette doppie / doppie a sinistra nel mio input, e sia US-ASCII che UTF-8 hanno lanciato MalformedInputException su di essi, ma ISO-8859-1 ha funzionato.


7

Ho anche riscontrato questa eccezione con un messaggio di errore,

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)

e ha scoperto che si verifica uno strano bug durante il tentativo di utilizzare

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

per scrivere un cast di String "orazg 54" da un tipo generico in una classe.

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

Questa stringa è di lunghezza 9 e contiene caratteri con i seguenti punti di codice:

111114 97122103 9 53 52 10

Tuttavia, se BufferedWriter nella classe viene sostituito con:

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

può scrivere con successo questa stringa senza eccezioni. Inoltre, se scrivo la stessa stringa creata dai caratteri, funziona ancora bene.

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

In precedenza non ho mai riscontrato alcuna eccezione durante l'utilizzo del primo BufferedWriter per scrivere stringhe. È uno strano bug che si verifica in BufferedWriter creato da java.nio.file.Files.newBufferedWriter (percorso, opzioni)


1
Questo è un po 'fuori tema, poiché l'OP parlava di lettura, piuttosto che di scrittura. Ho avuto un problema simile a causa di BufferedWriter.write (int), che tratta quell'int come un carattere e lo scrive direttamente nello stream. La soluzione è convertirlo manualmente in stringa e quindi scrivere.
malaverdiere

Questa è una risposta purtroppo sotto votata, Davvero un bel lavoro Tom. Mi chiedo se questo problema sia stato risolto nelle versioni successive di Java.
Ryboflavin


4

prova questo .. ho avuto lo stesso problema, sotto l'implementazione ha funzionato per me

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

quindi usa Reader dove vuoi.

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

Ho scritto quanto segue per stampare un elenco di risultati su standard out basato sui set di caratteri disponibili. Nota che ti dice anche quale riga fallisce da un numero di riga basato su 0 nel caso in cui stai risolvendo quale carattere sta causando problemi.

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

Ebbene, il problema è che Files.newBufferedReader(Path path)è implementato in questo modo:

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

quindi fondamentalmente non ha senso specificare a UTF-8meno che tu non voglia essere descrittivo nel tuo codice. Se vuoi provare un set di caratteri "più ampio" puoi provare con StandardCharsets.UTF_16, ma non puoi essere comunque sicuro al 100% di ottenere tutti i caratteri possibili.


-1

puoi provare qualcosa di simile, o semplicemente copiare e incollare sotto il pezzo.

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

Il gestore delle eccezioni può potenzialmente rendere il while(exception)ciclo per sempre se non trova mai un set di caratteri funzionante nell'array. Il gestore delle eccezioni dovrebbe rilanciare se viene raggiunta la fine dell'array e non viene trovato alcun set di caratteri funzionante. Inoltre, al momento in cui scrivo questa risposta ha ottenuto "-2" voti. L'ho ribaltato a "-1". Penso che il motivo per cui ha ottenuto voti negativi sia perché la spiegazione è insufficiente. Anche se capisco cosa fa il codice, altre persone potrebbero non farlo. Quindi un commento come "puoi provare qualcosa di simile" potrebbe non essere apprezzato da alcune persone.
mvanle

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.