Java FileReader-Codierungsproblem

130

Ich habe versucht, mit java.io.FileReader einige Textdateien zu lesen und in eine Zeichenfolge zu konvertieren, aber ich habe festgestellt, dass das Ergebnis falsch codiert und überhaupt nicht lesbar ist.

Hier ist meine Umgebung:

  • Windows 2003, Betriebssystemcodierung: CP1252

  • Java 5.0

Meine Dateien sind UTF-8-codiert oder CP1252-codiert, und einige von ihnen (UTF-8-codierte Dateien) können chinesische (nicht lateinische) Zeichen enthalten.

Ich benutze den folgenden Code, um meine Arbeit zu erledigen:

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

Der obige Code funktioniert nicht. Ich habe festgestellt, dass die FileReader-Codierung CP1252 ist, auch wenn der Text UTF-8-codiert ist. Aber das JavaDoc von java.io.FileReader sagt Folgendes:

Die Konstruktoren dieser Klasse gehen davon aus, dass die Standardzeichencodierung und die Standardgröße des Bytepuffers angemessen sind.

Bedeutet dies, dass ich die Zeichencodierung nicht selbst festlegen muss, wenn ich FileReader verwende? Aber ich habe derzeit falsch codierte Daten erhalten. Wie gehe ich richtig mit meiner Situation um? Vielen Dank.

Nybon
quelle
Sie sollten auch String.valueOf () in der Schleife verlieren und StringBuffer.append (char [], int, int) direkt verwenden. Dies erspart viel Kopieren des Zeichens []. Ersetzen Sie auch StringBuffer durch StringBuilder. Bei alledem geht es jedoch nicht um Ihre Frage.
Joachim Sauer
1
Ich hasse es, es zu sagen, aber haben Sie das JavaDoc direkt nach dem Teil gelesen, den Sie eingefügt haben? Sie wissen, der Teil mit der Aufschrift "Um diese Werte selbst anzugeben, erstellen Sie einen InputStreamReader in einem FileInputStream."
Powerlord
Vielen Dank für Ihren Kommentar. Eigentlich habe ich JavaDoc gelesen. Ich bin mir jedoch nicht sicher, ob ich diese Werte selbst angeben und zu "Erstellen eines InputStreamReader in einem FileInputStream" wechseln soll.
Nybon
Ja, wenn Sie wissen, dass sich die Datei in einer anderen als der Plattform-Standardcodierung befindet, müssen Sie dem InputStreamReader mitteilen, welche Datei verwendet werden soll.
Alan Moore

Antworten:

247

Ja, Sie müssen die Codierung der Datei angeben, die Sie lesen möchten.

Ja, dies bedeutet, dass Sie die Codierung der Datei kennen müssen , die Sie lesen möchten.

Nein, es gibt keine allgemeine Möglichkeit, die Codierung einer bestimmten "Nur-Text" -Datei zu erraten .

Die Ein-Argument-Konstruktoren von verwendenFileReader immer die Standardcodierung der Plattform, was im Allgemeinen eine schlechte Idee ist .

Seit Java 11 FileReaderhat auch Konstruktoren gewonnen, die eine Codierung akzeptieren: new FileReader(file, charset)und new FileReader(fileName, charset).

In früheren Versionen von Java müssen Sie verwenden .new InputStreamReader(new FileInputStream(pathToFile), <encoding>)

Joachim Sauer
quelle
1
InputStream ist = neuer FileInputStream (Dateiname); Hier habe ich Fehlerdatei nicht gefunden Fehler mit russischen Dateinamen
Bhanu Sharma
3
+1 für den Vorschlag, InputStreamReader zu verwenden, jedoch die Verwendung von Links in Codeblöcken macht es schwierig, den Code zu kopieren und einzufügen, wenn dies geändert werden kann, thx
Ferrybig
1
Wäre es "UTF-8" oder "UTF8" in den Codierungen. Laut der Java SE-Referenz zur Codierung wäre es "UTF8" , da InputStreamReaderes sich um eine java.ioKlasse handelt.
NobleUplift
9
@NobleUplift: Die sicherste Wette ist StandardCharsets.UTF_8, dass es dort keine Möglichkeit gibt, falsch zu tippen "UTF8".
Joachim Sauer
1
@JoachimSauer Eigentlich ist dies einer der Zwecke der Byte Order Mark, zusammen mit .. na ja .. Festlegung der Bytereihenfolge! :) Als solches finde ich es seltsam, dass Javas FileReader UTF-16 mit einer solchen Stückliste nicht automatisch erkennen kann ... Tatsächlich habe ich einmal eine geschrieben UnicodeFileReader, die genau das tut. Leider Closed Source, aber Google hat seinen UnicodeReader, der sehr ähnlich ist.
Stijn de Witt
79

FileReader Verwendet die Plattform-Standardcodierung von Java, die von den Systemeinstellungen des Computers abhängt, auf dem sie ausgeführt wird, und ist im Allgemeinen die beliebteste Codierung unter Benutzern in diesem Gebietsschema.

Wenn diese "beste Vermutung" nicht korrekt ist, müssen Sie die Codierung explizit angeben. Erlaubt FileReaderdies leider nicht (große Kontrolle in der API). Stattdessen müssen Sie new InputStreamReader(new FileInputStream(filePath), encoding)die Codierung aus Metadaten für die Datei verwenden und im Idealfall abrufen.

Michael Borgwardt
quelle
24
"Großes Versehen in der API" - danke für diese Erklärung - Ich habe mich gefragt, warum ich den Konstruktor, nach dem ich gesucht habe, nicht finden konnte! Prost John
Monojohnny
@Bhanu Sharma: Dies ist ein Codierungsproblem auf einer anderen Ebene. Überprüfen Sie, woher Sie den Dateinamen beziehen und ob die Codierung, die der Compiler verwendet, fest codiert ist.
Michael Borgwardt
1
@BhanuSharma: Probleme mit der Dateinamencodierung haben mit dieser Frage nichts zu tun. Lesen Sie eine der vielen vorhandenen Fragen zum Thema "Warum funktionieren Unicode-Dateinamen nicht in Java?". Spoiler: java.io-APIs wie FileReader verwenden Dateisystemaufrufe der C-Standardbibliothek, die Unicode unter Windows nicht unterstützen. Verwenden Sie stattdessen java.nio.
Bobince
1
" FileReaderVerwendet die Standardcodierung der Java-Plattform, die von den Systemeinstellungen des Computers abhängt, auf dem sie ausgeführt wird, und im Allgemeinen die beliebteste Codierung unter Benutzern in diesem Gebietsschema ist." Das würde ich nicht sagen. Zumindest von Windows. Aus seltsamen technischen / historischen Gründen ignoriert die JVM die Tatsache, dass Unicode die empfohlene Codierung unter Windows für "alle neuen Anwendungen" ist und verhält sich stattdessen immer so , als ob die als Fallback für Legacy-Apps konfigurierte Legacy-Codierung die "Plattform-Standardeinstellung" ist.
Stijn de Witt
6
Ich würde sogar so weit gehen zu sagen, dass wenn Ihre Java-App nicht jedes Mal , wenn sie in Dateien / Streams / Ressourcen liest oder schreibt, explizit Codierungen spezifiziert, diese kaputt ist , weil sie dann nie zuverlässig funktionieren kann .
Stijn de Witt
8

Seit Java 11 können Sie Folgendes verwenden:

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

Für Java 7+ doc können Sie Folgendes verwenden:

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

Hier sind alle Charsets doc

Wenn sich Ihre Datei beispielsweise in CP1252 befindet, verwenden Sie diese Methode

Charset.forName("windows-1252");

Hier finden Sie weitere kanonische Namen für Java-Codierungen für IO- und NIO- Dokumente

Wenn Sie nicht wissen , mit genau codiert , in einer Datei erhalten haben, können Sie einige Drittanbieter - Libs wie dieses Tool von Google verwenden diese , die recht ordentlich funktioniert.

Andreas Gelever
quelle
1

FileInputStream mit InputStreamReader ist besser als die direkte Verwendung von FileReader, da Sie mit letzterem keinen Codierungszeichensatz angeben können.

Hier ist ein Beispiel für die gemeinsame Verwendung von BufferedReader, FileInputStream und InputStreamReader, damit Sie Zeilen aus einer Datei lesen können.

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

Für eine andere als lateinische Sprache, zum Beispiel Kyrillisch, können Sie Folgendes verwenden:

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

und stellen Sie sicher, dass Ihre .txtDatei im UTF-8(aber nicht als Standard ANSI) Format gespeichert ist. Prost!

Iefimenko Ievgwn
quelle