Charset tout compris pour éviter «java.nio.charset.MalformedInputException: Input length = 1»?

93

Je crée un programme de comptage de mots simple en Java qui lit les fichiers texte d'un répertoire.

Cependant, je continue à recevoir l'erreur:

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

à partir de cette ligne de code:

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

Je sais que j'obtiens probablement cela parce que j'ai utilisé un Charsetqui n'incluait pas certains des caractères dans les fichiers texte, dont certains incluaient des caractères d'autres langues. Mais je veux inclure ces personnages.

J'ai appris plus tard aux JavaDocs que le Charsetest facultatif et utilisé uniquement pour une lecture plus efficace des fichiers, j'ai donc changé le code en:

BufferedReader reader = Files.newBufferedReader(file);

Mais certains fichiers lancent toujours le fichier MalformedInputException. Je ne sais pas pourquoi.

Je me demandais s'il existe un forfait tout compris Charsetqui me permettra de lire des fichiers texte avec de nombreux types de caractères différents ?

Merci.

Jonathan Lam
la source

Réponses:

79

Vous souhaitez probablement avoir une liste des encodages pris en charge. Pour chaque fichier, essayez chaque encodage tour à tour, peut-être en commençant par UTF-8. Chaque fois que vous attrapez le MalformedInputException, essayez le prochain encodage.

Dawood ibn Kareem
la source
43
J'ai essayé ISO-8859-1et ça marche bien. Je pense que c'est pour les personnages européens, ce qui est bien. UTF-16Cependant, je ne sais toujours pas pourquoi cela ne fonctionne pas.
Jonathan Lam
1
Si vous avez Notepad ++, vous pouvez essayer d'ouvrir le fichier texte et il vous indiquera l'encodage du fichier dans Menu. Vous pouvez ensuite adapter le code de manière corrigée si vous obtenez toujours un fichier de la même source.
JGFMK
@JonathanLam Eh bien, parce que si c'est encodé avec ISO-8859-1, alors ce n'est pas le cas UTF-16 . Ces encodages sont complètement différents. Un fichier ne peut pas être les deux.
Dawood ibn Kareem le
@DawoodsaysreinstateMonica Je crois que je voulais dire que j'étais surpris que l'UTF-16 ne fonctionne pas aussi bien qu'un fourre-tout pour les caractères européens comme ISO-8859-1 semble le faire. Mais merci pour l'info (même si six ans plus tard): P
Jonathan Lam
Sûr. UTF-16 contient tous les caractères européens. Mais ils sont représentés différemment de l'ISO-8859-1. Dans ISO-8859-1, tous les caractères sont représentés avec seulement 8 bits, vous êtes donc limité à 256 caractères possibles. En UTF-16, la plupart des caractères sont représentés avec 16 bits et certains caractères sont représentés avec 32 bits. Il y a donc beaucoup plus de caractères possibles en UTF-16, mais un fichier ISO-8859-1 ne nécessitera que la moitié de l'espace que les mêmes données utiliseraient en UTF-16.
Dawood ibn Kareem le
39

Création de BufferedReader à partir de Files.newBufferedReader

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

lors de l'exécution de l'application, il peut lever l'exception suivante:

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

Mais

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

fonctionne bien.

La différence est que, le premier utilise l'action par défaut CharsetDecoder.

L'action par défaut pour les erreurs de saisie malformée et de caractère non mappable consiste à les signaler .

tandis que ce dernier utilise l'action REPLACE.

cs.newDecoder().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE)
Xin Wang
la source
27

ISO-8859-1 est un jeu de caractères tout compris, dans le sens où il est garanti de ne pas lancer MalformedInputException. C'est donc bon pour le débogage, même si votre entrée n'est pas dans ce jeu de caractères. Alors:-

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

J'avais des caractères double-guillemets à droite / guillemets à gauche dans mon entrée, et US-ASCII et UTF-8 ont lancé MalformedInputException sur eux, mais ISO-8859-1 a fonctionné.

Tim Cooper
la source
6

J'ai également rencontré cette exception avec un message d'erreur,

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)

et a constaté qu'un bug étrange se produit lors de la tentative

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

pour écrire une chaîne "orazg 54" transtypée à partir d'un type générique dans une classe.

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

Cette chaîne est de longueur 9 contenant des caractères avec les points de code suivants:

111 114 97122103 9 53 52 10

Cependant, si BufferedWriter de la classe est remplacé par:

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

il peut écrire avec succès cette chaîne sans exceptions. De plus, si j'écris la même chaîne créée à partir des caractères, cela fonctionne toujours bien.

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

Auparavant, je n'avais jamais rencontré d'exception lors de l'utilisation du premier BufferedWriter pour écrire des chaînes. C'est un bug étrange qui se produit dans BufferedWriter créé à partir de java.nio.file.Files.newBufferedWriter (chemin, options)

À M
la source
1
C'est quelque peu hors sujet, car le PO parlait de lecture plutôt que d'écriture. J'ai eu un problème similaire en raison de BufferedWriter.write (int) - qui traite cet int comme un caractère et l'écrit directement dans le flux. La solution de contournement consiste à le convertir manuellement en chaîne, puis à écrire.
malaverdiere
C'est une réponse tristement sous-votée, vraiment beau travail Tom. Je me demande si cela a été résolu dans les versions ultérieures de Java.
Ryboflavin
5

ISO_8859_1 a travaillé pour moi! Je lisais un fichier texte avec des valeurs séparées par des virgules

Shahid Hussain Abbasi
la source
3

J'ai écrit ce qui suit pour imprimer une liste de résultats à la norme en fonction des jeux de caractères disponibles. Notez qu'il vous indique également quelle ligne échoue à partir d'un numéro de ligne basé sur 0 au cas où vous dépanneriez le caractère qui cause des problèmes.

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
la source
3

essayez ceci .. j'ai eu le même problème, la mise en œuvre ci-dessous a fonctionné pour moi

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

puis utilisez Reader où vous le souhaitez.

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
la source
0

Eh bien, le problème est que Files.newBufferedReader(Path path) est implémenté comme ceci:

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

Donc, fondamentalement, il est inutile de spécifier à UTF-8moins que vous ne vouliez être descriptif dans votre code. Si vous voulez essayer un jeu de caractères "plus large", vous pouvez essayer avec StandardCharsets.UTF_16, mais vous ne pouvez pas être sûr à 100% d'obtenir tous les caractères possibles de toute façon.

francesco foresti
la source
-1

vous pouvez essayer quelque chose comme ça, ou simplement copier et coller la pièce ci-dessous.

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
la source
Le gestionnaire d'exceptions peut potentiellement rendre la while(exception)boucle pour toujours s'il ne trouve jamais un jeu de caractères fonctionnel dans le tableau. Le gestionnaire d'exceptions doit renvoyer si la fin du tableau est atteinte et qu'aucun jeu de caractères fonctionnel n'est trouvé. De plus, au moment de la rédaction de cet article, cette réponse comptait "-2" votes. Je l'ai voté à "-1". Je pense que la raison pour laquelle il a obtenu des votes négatifs est que les explications sont insuffisantes. Bien que je comprenne ce que fait le code, d'autres personnes ne le peuvent pas. Donc, un commentaire comme "vous pouvez essayer quelque chose comme ça" peut ne pas être apprécié par certaines personnes.
mvanle le
-1

UTF-8 fonctionne pour moi avec des caractères polonais

Adriano
la source