Eine Datei in UTF-8 mit FileWriter (Java) schreiben?

79

Ich habe den folgenden Code, möchte jedoch, dass er als UTF-8-Datei für die Verarbeitung von Fremdzeichen geschrieben wird. Gibt es eine Möglichkeit, dies zu tun, muss ein Parameter vorhanden sein?

Ich würde mich sehr über Ihre Hilfe freuen. Vielen Dank.

try {
  BufferedReader reader = new BufferedReader(new FileReader("C:/Users/Jess/My Documents/actresses.list"));
  writer = new BufferedWriter(new FileWriter("C:/Users/Jess/My Documents/actressesFormatted.csv"));
  while( (line = reader.readLine()) != null) {
    //If the line starts with a tab then we just want to add a movie
    //using the current actor's name.
    if(line.length() == 0)
      continue;
    else if(line.charAt(0) == '\t') {
      readMovieLine2(0, line, surname.toString(), forename.toString());
    } //Else we've reached a new actor
    else {
      readActorName(line);
    }
  }
} catch (IOException e) {
  e.printStackTrace();
}
user1280970
quelle

Antworten:

76

Sichere Codierungskonstruktoren

Es ist schwierig, Java dazu zu bringen, Sie ordnungsgemäß über Codierungsfehler zu benachrichtigen. Sie müssen den ausführlichsten und leider den am wenigsten verwendeten der vier alternativen Konstruktoren für jeden von InputStreamReaderund verwenden OutputStreamWriter, um eine ordnungsgemäße Ausnahme für einen Codierungsfehler zu erhalten.

Stellen Sie für Datei-E / A immer sicher, dass Sie immer das zweite Argument für beide OutputStreamWriterund InputStreamReaderdas ausgefallene Encoder-Argument verwenden:

  Charset.forName("UTF-8").newEncoder()

Es gibt andere, noch schickere Möglichkeiten, aber keine der drei einfacheren Möglichkeiten funktioniert für die Ausnahmebehandlung. Diese tun:

 OutputStreamWriter char_output = new OutputStreamWriter(
     new FileOutputStream("some_output.utf8"),
     Charset.forName("UTF-8").newEncoder() 
 );

 InputStreamReader char_input = new InputStreamReader(
     new FileInputStream("some_input.utf8"),
     Charset.forName("UTF-8").newDecoder() 
 );

Wie zum Laufen mit

 $ java -Dfile.encoding=utf8 SomeTrulyRemarkablyLongcLassNameGoeShere

Das Problem ist, dass dadurch nicht das vollständige Encoder-Argument für die Zeichenströme verwendet wird und Sie erneut Codierungsprobleme verpassen.

Längeres Beispiel

Hier ist ein längeres Beispiel, das einen Prozess anstelle einer Datei verwaltet, in dem zwei verschiedene Eingabebyte-Streams und ein Ausgabebyte-Stream mit vollständiger Ausnahmebehandlung in UTF-8-Zeichenströme umgewandelt werden :

 // this runs a perl script with UTF-8 STD{IN,OUT,ERR} streams
 Process
 slave_process = Runtime.getRuntime().exec("perl -CS script args");

 // fetch his stdin byte stream...
 OutputStream
 __bytes_into_his_stdin  = slave_process.getOutputStream();

 // and make a character stream with exceptions on encoding errors
 OutputStreamWriter
   chars_into_his_stdin  = new OutputStreamWriter(
                             __bytes_into_his_stdin,
         /* DO NOT OMIT! */  Charset.forName("UTF-8").newEncoder()
                         );

 // fetch his stdout byte stream...
 InputStream
 __bytes_from_his_stdout = slave_process.getInputStream();

 // and make a character stream with exceptions on encoding errors
 InputStreamReader
   chars_from_his_stdout = new InputStreamReader(
                             __bytes_from_his_stdout,
         /* DO NOT OMIT! */  Charset.forName("UTF-8").newDecoder()
                         );

// fetch his stderr byte stream...
 InputStream
 __bytes_from_his_stderr = slave_process.getErrorStream();

 // and make a character stream with exceptions on encoding errors
 InputStreamReader
   chars_from_his_stderr = new InputStreamReader(
                             __bytes_from_his_stderr,
         /* DO NOT OMIT! */  Charset.forName("UTF-8").newDecoder()
                         );

Jetzt haben Sie drei Zeichenströme , dass alle raise Ausnahmefehler auf kodieren jeweils genannt chars_into_his_stdin, chars_from_his_stdoutund chars_from_his_stderr.

Dies ist nur geringfügig komplizierter als das, was Sie für Ihr Problem benötigen, dessen Lösung ich in der ersten Hälfte dieser Antwort gegeben habe. Der entscheidende Punkt ist, dass dies die einzige Möglichkeit ist, Codierungsfehler zu erkennen.

Lass mich nur nicht damit anfangen PrintStream, Ausnahmen zu essen.

tchrist
quelle
1
Tolle Antwort, aber ich denke, es gibt einen kleinen Fehler - InputStreamReader char_input = new InputStreamWritersollte lauten: InputStreamReader char_input = new InputStreamReader und der InputStreamReaderKonstruktor nimmt ein CharsetDecoder, kein CharsetEncoder.
Mark Rhodes
Aber ist das ein echtes Problem, was UTF-8 nicht darstellen kann? Ich dachte, es könnte alles codieren.
Paul Taylor
Wenn Sie sich über Streams beschweren möchten, die Ausnahmen essen, versuchen Sie CipherInputStream, diese zu entfernen BadPaddingException, auch wenn sie von einem authentifizierten Chiffrestream erstellt wurden :(
Maarten Bodewes
Ich habe einen kleinen Fehler in Ihrem Code gefunden: "Charset.forName (" UTF-8 "). NewEncoder ()" für "InputStreamReader" sollte "Charset.forName (" UTF-8 "). NewDecoder ()" sein. Also "Decoder" statt "Encoder". Aber trotzdem, danke für diese nette Antwort und +1. :)
Codepleb
2
(Das gesamte Java IO-System war schon immer ein Chaos. Sollte komplett überarbeitet werden, wie Joda Time überarbeitete Daten.)
Tuntable
54

Ditch FileWriterund FileReader, die genau deshalb nutzlos sind, weil Sie die Codierung nicht angeben können. Verwenden Sie stattdessen

new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)

und

new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);

Michael Borgwardt
quelle
12
Wenn Sie nicht nur das sehr ausführliche Charset.forName("UTF-8").newDecoder()Argument (oder ein ausgefalleneres Konstrukt) verwenden "UTF-8", werden Sie nicht ordnungsgemäß über Codierungsfehler informiert (lesen Sie: Ausnahmen werden unterdrückt und Codierungsfehler werden auf mysteriöse Weise ausgeblendet).
Tchrist
3
new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8 )
Abdull
46

Sie müssen die OutputStreamWriterKlasse als Writer-Parameter für Ihre verwenden BufferedWriter. Es akzeptiert eine Codierung. Überprüfen Sie Javadocs dafür.

Etwas wie folgt:

BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
    new FileOutputStream("jedis.txt"), "UTF-8"
));

Oder Sie können die aktuelle Systemcodierung mit der Systemeigenschaft file.encodingauf UTF-8 setzen.

java -Dfile.encoding=UTF-8 com.jediacademy.Runner arg1 arg2 ...

Sie können es auch zur Laufzeit als Systemeigenschaft festlegen, System.setProperty(...)wenn Sie es nur für diese bestimmte Datei benötigen, aber in einem solchen Fall würde ich das bevorzugen OutputStreamWriter.

Wenn Sie die Systemeigenschaft festlegen, können Sie FileWriterdavon ausgehen, dass UTF-8 als Standardcodierung für Ihre Dateien verwendet wird. In diesem Fall für alle Dateien, die Sie lesen und schreiben.

BEARBEITEN

  • Ab API 19 können Sie den String "UTF-8" durch ersetzen StandardCharsets.UTF_8

  • Wie in den Kommentaren von tchrist unten vorgeschlagen , müssen Sie, wenn Sie Codierungsfehler in Ihrer Datei erkennen möchten, den OutputStreamWriterAnsatz verwenden und den Konstruktor verwenden, der einen Zeichensatzcodierer empfängt.

    Etwas wie

    CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
    encoder.onMalformedInput(CodingErrorAction.REPORT);
    encoder.onUnmappableCharacter(CodingErrorAction.REPORT);
    BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("jedis.txt"),encoder));
    

    Sie können zwischen Aktionen wählen IGNORE | REPLACE | REPORT

Außerdem wurde diese Frage schon beantwortet hier .

Edwin Dalorzo
quelle
Das ist nicht genug. Sie brauchen auch eine InputStreamReader(InputStream in, CharsetDecoder dec), so dass das letzte Argument ist Charset.forName("UTF-8").newDecoder().
Tchrist
1
Eingabecodierungsfehler werden stillschweigend gelöscht, wenn Sie dies tun.
Tchrist
Es ist kein Encoder erforderlich. Der Konstruktor akzeptiert entweder einen String, einen Zeichensatz oder einen Encoder in beiden Eingabe- / Ausgabeklassen. Ich bin mir nicht sicher, was du mit deinem Kommentar meinst. Können Sie das bitte näher erläutern?
Edwin Dalorzo
3
@edalorzo Wenn Sie die vier verschiedenen {In,Out}putStream{Reader,Writer}Konstruktoren auf fehlerhafte Daten testen , werden Sie feststellen, dass drei von ihnen alle Ausnahmen maskieren , die sich aus Codierungsfehlern ergeben sollten , und nur die vierte Form liefert sie Ihnen korrekt. Darum geht es Charset.forName("UTF-8").newDecoder(). Ich erkläre dies ein wenig in meiner Antwort.
Tchrist
1
Ja, das ist viel besser. Es ist viel häufiger mit Eingabecodierung Fehler , bei denen dies aufgeht , als es mit einer Leistung kommt (zumindest , wenn es sich um eine UTF Form ist: 8-Bit - Ausgabe - Codierungen sind immer lose-lose . In Unicode) Sie jedoch in der Theorie können sie noch entstehen bei der Ausgabe, da Java zulässt, dass ungepaarte Surrogate in Zeichenfolgen im Speicher vorhanden sind (dies muss der Fall sein; dies ist kein Fehler!), aber kein konformer UTF- {8,16,32} Ausgabecodierer darf sie bei der Ausgabe erzeugen.
Tchrist
8

Seit Java 11 können Sie:

FileWriter fw = new FileWriter("filename.txt", Charset.forName("utf-8"));
mortensi
quelle
7

Seit Java 7 gibt es eine einfache Möglichkeit, die Zeichencodierung von BufferedWriter und BufferedReaders zu handhaben. Sie können einen BufferedWriter direkt mithilfe der Files-Klasse erstellen, anstatt verschiedene Instanzen von Writer zu erstellen. Sie können einfach einen BufferedWriter erstellen, der die Zeichenkodierung berücksichtigt, indem Sie Folgendes aufrufen:

Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8);

Weitere Informationen finden Sie in JavaDoc:

Lars Briem
quelle
5

Mit chinesischem Text habe ich versucht, das Charset UTF-16 zu verwenden, und zum Glück funktioniert es.

Hoffe das könnte helfen!

PrintWriter out = new PrintWriter( file, "UTF-16" );
Phuong
quelle
kann mit UTF-32 versuchen
anson
1

OK, es ist jetzt 2019 und ab Java 11 haben Sie einen Konstruktor mit Charset:

FileWriter​(String fileName, Charset charset)

Leider können wir die Bytepuffergröße immer noch nicht ändern und sie ist auf 8192 festgelegt. ( Https://www.baeldung.com/java-filewriter )

Code đờ
quelle
0

Verwenden Sie OutputStream anstelle von FileWriter, um den Codierungstyp festzulegen

// file is your File object where you want to write you data 
OutputStream outputStream = new FileOutputStream(file);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8");
outputStreamWriter.write(json); // json is your data 
outputStreamWriter.flush();
outputStreamWriter.close();
Zakaria
quelle
-3

Meiner Meinung nach

Wenn Sie schreiben möchten , folgen Sie der folgenden Art von UTF-8. Sie sollten ein Byte-Array erstellen. Dann können Sie Folgendes tun: byte[] by=("<?xml version=\"1.0\" encoding=\"utf-8\"?>"+"Your string".getBytes();

Anschließend können Sie jedes Byte in die von Ihnen erstellte Datei schreiben. Beispiel:

OutputStream f=new FileOutputStream(xmlfile);
    byte[] by=("<?xml version=\"1.0\" encoding=\"utf-8\"?>"+"Your string".getBytes();
    for (int i=0;i<by.length;i++){
    byte b=by[i];
    f.write(b);

    }
    f.close();
Phan Ngọc Hoàng Dương
quelle
Willkommen bei Stack Overflow! Während dieses Code-Snippet die Frage lösen kann, hilft eine Erklärung wirklich dabei, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage in Zukunft für Leser beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen. Bitte versuchen Sie auch, Ihren Code nicht mit erklärenden Kommentaren zu überfüllen. Dies verringert die Lesbarkeit sowohl des Codes als auch der Erklärungen!
Isiah Meadows