¿Escribir un archivo en UTF-8 usando FileWriter (Java)?

82

Sin embargo, tengo el siguiente código, quiero que se escriba como un archivo UTF-8 para manejar caracteres extranjeros. ¿Hay alguna forma de hacer esto, es necesario tener un parámetro?

Realmente agradecería su ayuda con esto. Gracias.

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

Respuestas:

77

Constructores de codificación segura

Conseguir que Java le notifique correctamente los errores de codificación es complicado. Debe utilizar el más detallado y, por desgracia, el menos utilizado de los cuatro constructores alternativos para cada uno de InputStreamReadery OutputStreamWriterpara recibir una excepción adecuada en un error de codificación.

Para la E / S de archivos, asegúrese siempre de usar siempre como segundo argumento para ambos OutputStreamWritery InputStreamReaderel elegante argumento del codificador:

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

Hay otras posibilidades aún más elegantes, pero ninguna de las tres posibilidades más simples funciona para el manejo de excepciones. Estos hacen:

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

En cuanto a correr con

 $ java -Dfile.encoding=utf8 SomeTrulyRemarkablyLongcLassNameGoeShere

El problema es que no utilizará la forma de argumento del codificador completo para los flujos de caracteres, por lo que volverá a perder los problemas de codificación.

Ejemplo más largo

Aquí hay un ejemplo más largo, este que administra un proceso en lugar de un archivo, donde promovemos dos flujos de bytes de entrada diferentes y un flujo de bytes de salida, todos a flujos de caracteres UTF-8 con manejo completo de excepciones :

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

Ahora tiene tres corrientes de carácter que toda excepción aumento en la codificación de errores, llamados respectivamente chars_into_his_stdin, chars_from_his_stdouty chars_from_his_stderr.

Esto es solo un poco más complicado de lo que necesita para su problema, cuya solución di en la primera mitad de esta respuesta. El punto clave es que esta es la única forma de detectar errores de codificación.

No me hagas hablar de PrintStreamlas excepciones alimenticias.

tchrist
fuente
1
Gran respuesta, pero creo que hay un error menor: InputStreamReader char_input = new InputStreamWriterdebería leer:, InputStreamReader char_input = new InputStreamReader y el InputStreamReaderconstructor toma un CharsetDecoder, no un CharsetEncoder.
Mark Rhodes
Pero, ¿es esto un problema real, lo que no es posible de representar para UTF-8? Pensé que podría codificar cualquier cosa.
Paul Taylor
Si desea quejarse de las excepciones de consumo de Streams, intente CipherInputStream, que elimine BadPaddingException's, incluso si son creados por un flujo de cifrado autenticado :(
Maarten Bodewes
Encontré un pequeño error en su código: "Charset.forName (" UTF-8 "). NewEncoder ()" para "InputStreamReader" debería ser "Charset.forName (" UTF-8 "). NewDecoder ()". Entonces "decodificador" en lugar de "codificador". Pero de todos modos, gracias por esta linda respuesta y +1. :)
codepleb
2
(Todo el sistema Java IO siempre ha sido un desastre. Debería ser completamente reelaborado como las fechas reelaboradas de Joda Time.)
Tuntable
56

Deshazte de FileWritery FileReader, que son inútiles precisamente porque no te permiten especificar la codificación. En su lugar, use

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

y

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

Michael Borgwardt
fuente
12
Si no usa el Charset.forName("UTF-8").newDecoder()argumento muy detallado (o alguna construcción más sofisticada) en lugar de solo "UTF-8", no se le notificará adecuadamente de los errores de codificación (lea: se suprimirán las excepciones y ocultará misteriosamente los errores de codificación).
tchrist
3
new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8 )
Abdull
46

Debe usar la OutputStreamWriterclase como parámetro de escritura para su BufferedWriter. Acepta una codificación. Revise los javadocs para ello.

Algo así:

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

O puede establecer la codificación del sistema actual con la propiedad del sistema file.encodingen UTF-8.

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

También puede configurarlo como una propiedad del sistema en tiempo de ejecución System.setProperty(...)si solo lo necesita para este archivo específico, pero en un caso como este, creo que preferiría el OutputStreamWriter.

Al establecer la propiedad del sistema, puede usar FileWritery esperar que use UTF-8 como la codificación predeterminada para sus archivos. En este caso para todos los archivos que lee y escribe.

EDITAR

  • A partir de API 19, puede reemplazar la cadena "UTF-8" por StandardCharsets.UTF_8

  • Como lo sugiere tchrist en los comentarios a continuación , si tiene la intención de detectar errores de codificación en su archivo, se verá obligado a usar el OutputStreamWriterenfoque y usar el constructor que recibe un codificador de juego de caracteres.

    Algo parecido

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

    Puedes elegir entre acciones IGNORE | REPLACE | REPORT

Además, esta pregunta ya fue respondida aquí .

Edwin Dalorzo
fuente
Eso no es suficiente. También necesita un InputStreamReader(InputStream in, CharsetDecoder dec), de modo que el último argumento sea Charset.forName("UTF-8").newDecoder().
tchrist
1
Los errores de codificación de entrada se eliminarán silenciosamente si lo hace así.
tchrist
No es necesario un codificador. El constructor acepta una cadena, un conjunto de caracteres o un codificador en ambas clases de entrada / salida. No estoy seguro de lo que quiere decir con su comentario. ¿Puede explicarme, por favor?
Edwin Dalorzo
3
@edalorzo Si prueba los cuatro {In,Out}putStream{Reader,Writer}constructores diferentes en datos erróneos, descubrirá que tres de ellos enmascaran todas las excepciones que deberían surgir de errores de codificación, y solo la cuarta forma se las entrega correctamente. Ese es el que involucra Charset.forName("UTF-8").newDecoder(). Explico esto un poco en mi respuesta.
tchrist
1
Sí, eso es mucho mejor. Es mucho más frecuente que se produzcan errores de codificación de entrada en los que surja esto que en la salida (al menos si es una forma UTF: las codificaciones de salida de 8 bits siempre se pierden en Unicode). Sin embargo, en teoría, aún puede incurrir en ellas. en la salida porque Java permite que existan sustitutos no apareados en cadenas en la memoria ( tiene que hacerlo; ¡esto no es un error!), pero ningún codificador de salida UTF- {8,16,32} compatible puede producirlos en la salida.
tchrist
9

Desde Java 11 puedes hacer:

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

Desde Java 7, existe una manera fácil de manejar la codificación de caracteres de BufferedWriter y BufferedReaders. Puede crear un BufferedWriter directamente utilizando la clase Archivos en lugar de crear varias instancias de Writer. Simplemente puede crear un BufferedWriter, que considera la codificación de caracteres, llamando a:

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

Puede encontrar más información al respecto en JavaDoc:

Lars Briem
fuente
5

Con texto en chino, intenté usar Charset UTF-16 y afortunadamente funcionó.

¡Espero que esto pueda ayudar!

PrintWriter out = new PrintWriter( file, "UTF-16" );
Phuong
fuente
puede probar con UTF-32
anson
1

OK, es 2019 ahora, y desde Java 11 tienes un constructor con Charset:

FileWriter​(String fileName, Charset charset)

Desafortunadamente, todavía no podemos modificar el tamaño del búfer de bytes y está configurado en 8192. ( https://www.baeldung.com/java-filewriter )

código đờ
fuente
0

use OutputStream en lugar de FileWriter para establecer el tipo de codificación

// 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
fuente
-3

en mi opinión

Si desea escribir, siga el tipo UTF-8 . Debe crear una matriz de bytes. Luego, puede hacer lo siguiente: byte[] by=("<?xml version=\"1.0\" encoding=\"utf-8\"?>"+"Your string".getBytes();

Luego, puede escribir cada byte en el archivo que creó. Ejemplo:

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
fuente
¡Bienvenido a Stack Overflow! Si bien este fragmento de código puede resolver la pregunta, incluir una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo a la pregunta para los lectores en el futuro, y es posible que esas personas no conozcan los motivos de su sugerencia de código. Por favor, trate también de no llenar su código con comentarios explicativos, ¡esto reduce la legibilidad tanto del código como de las explicaciones!
Isiah Meadows