Problème d'encodage Java FileReader

130

J'ai essayé d'utiliser java.io.FileReader pour lire certains fichiers texte et les convertir en une chaîne, mais j'ai trouvé que le résultat était mal encodé et pas du tout lisible.

Voici mon environnement:

  • Windows 2003, encodage du système d'exploitation: CP1252

  • Java 5.0

Mes fichiers sont codés UTF-8 ou CP1252, et certains d'entre eux (fichiers codés UTF-8) peuvent contenir des caractères chinois (non latins).

J'utilise le code suivant pour faire mon travail:

   private static String readFileAsString(String filePath)
    throws java.io.IOException{
        StringBuffer fileData = new StringBuffer(1000);
        FileReader reader = new FileReader(filePath);
        //System.out.println(reader.getEncoding());
        BufferedReader reader = new BufferedReader(reader);
        char[] buf = new char[1024];
        int numRead=0;
        while((numRead=reader.read(buf)) != -1){
            String readData = String.valueOf(buf, 0, numRead);
            fileData.append(readData);
            buf = new char[1024];
        }
        reader.close();
        return fileData.toString();
    }

Le code ci-dessus ne fonctionne pas. J'ai trouvé que l'encodage de FileReader est CP1252 même si le texte est encodé UTF-8. Mais le JavaDoc de java.io.FileReader dit que:

Les constructeurs de cette classe supposent que le codage de caractères par défaut et la taille par défaut du tampon d'octets sont appropriés.

Cela signifie-t-il que je ne suis pas obligé de définir moi-même le codage des caractères si j'utilise FileReader? Mais j'ai actuellement des données mal encodées, quelle est la bonne façon de gérer ma situation? Merci.

nybon
la source
Vous devez également perdre le String.valueOf () à l'intérieur de la boucle et utiliser directement StringBuffer.append (char [], int, int). Cela économise beaucoup de copie du char []. Remplacez également StringBuffer par StringBuilder. Rien de tout cela ne concerne votre question, «cependant.
Joachim Sauer
1
Je déteste le dire, mais avez-vous lu le JavaDoc juste après la partie que vous avez collée? Vous savez, la partie qui dit "Pour spécifier ces valeurs vous-même, construisez un InputStreamReader sur un FileInputStream."?
Powerlord
Merci pour votre commentaire, en fait j'ai lu le JavaDoc, mais ce que je ne suis pas sûr, c'est si je devrais ou non spécifier ces valeurs moi-même, et passer à "construire un InputStreamReader sur un FileInputStream".
nybon
Oui, si vous savez que le fichier est différent de l'encodage par défaut de la plateforme, vous devez indiquer à InputStreamReader lequel utiliser.
Alan Moore

Réponses:

248

Oui, vous devez spécifier le codage du fichier que vous souhaitez lire.

Oui, cela signifie que vous devez connaître le codage du fichier que vous souhaitez lire.

Non, il n'existe aucun moyen général de deviner le codage d'un fichier "texte brut" donné.

Les constructeurs à un argumentFileReader utilisent toujours le codage par défaut de la plateforme, ce qui est généralement une mauvaise idée .

Depuis Java 11 FileReadera également gagné des constructeurs qui acceptent un encodage: new FileReader(file, charset)et new FileReader(fileName, charset).

Dans les versions antérieures de java, vous devez utiliser .new InputStreamReader(new FileInputStream(pathToFile), <encoding>)

Joachim Sauer
la source
1
InputStream est = new FileInputStream (nom de fichier); ici j'ai obtenu le fichier d'erreur non trouvé erreur avec le nom de fichier russe
Bhanu Sharma
3
+1 pour la suggestion d'utiliser InputStreamReader, mais l'utilisation de liens dans des blocs de code rend difficile la copie et le collage du code, si cela peut être changé,
merci
1
Serait-ce "UTF-8" ou "UTF8" dans les encodages. D'après la référence Java SE sur l'encodage , puisqu'il InputStreamReaders'agit d'une java.ioclasse, ce serait "UTF8"?
NobleUplift
9
@NobleUplift: le pari le plus sûr est StandardCharsets.UTF_8qu'il n'y a aucune chance de se tromper ;-) Mais oui, si vous y allez avec une chaîne, ce "UTF8"serait correct (bien que je semble me souvenir que cela acceptera les deux sens).
Joachim Sauer
1
@JoachimSauer En fait, c'est l'un des objectifs de Byte Order Mark, avec .. eh bien .. établir l'ordre des octets! :) En tant que tel, je trouve étrange que FileReader de Java ne soit pas capable de détecter automatiquement UTF-16 qui a une telle nomenclature ... En fait, j'ai déjà écrit un UnicodeFileReaderqui fait exactement cela. Malheureusement source fermée, mais Google a son UnicodeReader qui est très similaire.
Stijn de Witt
79

FileReader utilise le codage par défaut de la plate-forme Java, qui dépend des paramètres système de l'ordinateur sur lequel il s'exécute et est généralement le codage le plus populaire parmi les utilisateurs de cette locale.

Si cette "meilleure estimation" n'est pas correcte, vous devez spécifier le codage explicitement. Malheureusement, FileReaderne permet pas cela (omission majeure dans l'API). Au lieu de cela, vous devez utiliser new InputStreamReader(new FileInputStream(filePath), encoding)et idéalement obtenir le codage à partir des métadonnées du fichier.

Michael Borgwardt
la source
24
"Oubli majeur dans l'API" - merci pour cette explication - je me demandais pourquoi je ne trouvais pas le constructeur que je recherchais! Cheers John
monojohnny
@Bhanu Sharma: c'est un problème d'encodage à un niveau différent, vérifiez d'où vous obtenez le nom de fichier, et s'il est codé en dur quel encodage le compilateur utilise.
Michael Borgwardt
1
@BhanuSharma: les problèmes d'encodage des noms de fichiers n'ont rien à voir avec cette question. Consultez l'une des nombreuses questions existantes «Pourquoi les noms de fichiers Unicode ne fonctionnent-ils pas en Java». Spoiler: les API java.io comme FileReader utilisent les appels de système de fichiers de la bibliothèque standard C, qui ne peuvent pas prendre en charge Unicode sous Windows; envisagez d'utiliser java.nio à la place.
bobince
1
" FileReaderutilise le codage par défaut de la plate-forme Java, qui dépend des paramètres système de l'ordinateur sur lequel il s'exécute et est généralement le codage le plus populaire parmi les utilisateurs de cette locale." Je ne dirais pas ça. Au moins de Windows. Pour des raisons techniques / historiques étranges, la JVM ignore le fait qu'Unicode est l' encodage recommandé sur Windows pour `` toutes les nouvelles applications '' et agit à la place toujours comme si l'encodage hérité configuré comme solution de secours pour les applications héritées était le `` par défaut de la plate-forme ''.
Stijn de Witt
6
J'irais même jusqu'à dire que si votre application Java ne spécifie pas explicitement les encodages à chaque fois qu'elle lit ou écrit dans des fichiers / flux / ressources, elle est cassée , car elle ne peut jamais fonctionner de manière fiable.
Stijn de Witt
8

Depuis Java 11, vous pouvez utiliser cela:

public FileReader(String fileName, Charset charset) throws IOException;
Radoslav Ivanov
la source
6

Pour Java doc vous pouvez utiliser ceci:

BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);

Voici tous les documents sur les jeux de caractères

Par exemple, si votre fichier est en CP1252, utilisez cette méthode

Charset.forName("windows-1252");

Voici d'autres noms canoniques pour les encodages Java à la fois pour IO et NIO doc

Si vous ne savez pas avec l'encodage exact que vous avez dans un fichier, vous pouvez utiliser des bibliothèques tierces comme cet outil de Google, ce qui fonctionne assez bien.

Andreas Gelever
la source
1

FileInputStream avec InputStreamReader est mieux que d'utiliser directement FileReader, car ce dernier ne vous permet pas de spécifier un jeu de caractères d'encodage.

Voici un exemple utilisant BufferedReader, FileInputStream et InputStreamReader ensemble, afin que vous puissiez lire les lignes d'un fichier.

List<String> words = new ArrayList<>();
List<String> meanings = new ArrayList<>();
public void readAll( ) throws IOException{
    String fileName = "College_Grade4.txt";
    String charset = "UTF-8";
    BufferedReader reader = new BufferedReader(
        new InputStreamReader(
            new FileInputStream(fileName), charset)); 

    String line; 
    while ((line = reader.readLine()) != null) { 
        line = line.trim();
        if( line.length() == 0 ) continue;
        int idx = line.indexOf("\t");
        words.add( line.substring(0, idx ));
        meanings.add( line.substring(idx+1));
    } 
    reader.close();
}
Guangtong Shen
la source
0

Pour une autre langue latine par exemple cyrillique, vous pouvez utiliser quelque chose comme ceci:

FileReader fr = new FileReader("src/text.txt", StandardCharsets.UTF_8);

et assurez-vous que votre .txtfichier est enregistré au format UTF-8(mais pas par défaut ANSI). À votre santé!

Iefimenko Ievgwn
la source