2009-04-19 17 views
10

Utilizo el siguiente código para guardar caracteres chinos en un archivo .txt, pero cuando lo abrí con Wordpad, no pude leerlo.Cómo guardar caracteres chinos para archivar con java?

StringBuffer Shanghai_StrBuf = new StringBuffer("\u4E0A\u6D77"); 
boolean Append = true; 

FileOutputStream fos; 
fos = new FileOutputStream(FileName, Append); 
for (int i = 0;i < Shanghai_StrBuf.length(); i++) { 
    fos.write(Shanghai_StrBuf.charAt(i)); 
} 
fos.close(); 

¿Qué puedo hacer? Sé que si corto y pego caracteres chinos en Wordpad, puedo guardarlo en un archivo .txt. ¿Cómo hago eso en Java?

+0

posible duplicado de [Qué es la codificación de caracteres y por qué debería molestarme con él] (http://stackoverflow.com/questions/10611455/what-is-character-encoding-and -por qué-debería-molestar-con-eso) – Raedwald

Respuesta

10

Hay varios factores en juego aquí:

  • Los archivos de texto no tienen metadatos intrínsecos para describir su codificación (para todo lo que se habla de impuestos de escuadras angulares, hay motivos XML es popular)
  • La codificación por defecto para Windows sigue siendo una de 8 bits (o doublebyte) carácter "ANSI" set con un rango limitado de valores - archivos de texto escritos en este formato no son portátiles
  • para contar un archivo Unicode de una Archivo ANSI, las aplicaciones de Windows se basan en la presencia de un byte order mark al comienzo del archivo (not strictly true - Raymond Chen explains). En teoría, la lista de materiales está allí para decirle el endianess (orden de bytes) de los datos. Para UTF-8, aunque solo hay un orden de bytes, las aplicaciones de Windows dependen de los bytes del marcador para descubrir automáticamente que es Unicode (aunque observará que el Bloc de notas tiene una opción de codificación en sus diálogos de abrir/guardar).
  • Es incorrecto decir que Java está roto porque no escribe una BOM UTF-8 automáticamente. En sistemas Unix, sería un error escribir una BOM en un archivo de script, por ejemplo, y muchos sistemas Unix usan UTF-8 como su codificación predeterminada. Hay momentos en los que no desea que en Windows, o bien, como cuando estás añadir datos a un archivo existente: fos = new FileOutputStream(FileName,Append);

Aquí es un método fiable de añadir datos UTF-8 en un archivo:

private static void writeUtf8ToFile(File file, boolean append, String data) 
     throws IOException { 
    boolean skipBOM = append && file.isFile() && (file.length() > 0); 
    Closer res = new Closer(); 
    try { 
     OutputStream out = res.using(new FileOutputStream(file, append)); 
     Writer writer = res.using(new OutputStreamWriter(out, Charset 
      .forName("UTF-8"))); 
     if (!skipBOM) { 
     writer.write('\uFEFF'); 
     } 
     writer.write(data); 
    } finally { 
     res.close(); 
    } 
    } 

Uso:

public static void main(String[] args) throws IOException { 
    String chinese = "\u4E0A\u6D77"; 
    boolean append = true; 
    writeUtf8ToFile(new File("chinese.txt"), append, chinese); 
    } 

Nota: si el archivo ya existía y se optó por añadir y los datos existentes no era codificación UTF-8, la única cosa que el código w crear mal es un desastre.

Aquí es el tipo Closer utilizado en este código:

public class Closer implements Closeable { 
    private Closeable closeable; 

    public <T extends Closeable> T using(T t) { 
    closeable = t; 
    return t; 
    } 

    @Override public void close() throws IOException { 
    if (closeable != null) { 
     closeable.close(); 
    } 
    } 
} 

Este código hace una mejor estimación de estilo de Windows acerca de cómo leer el archivo basado en marcas de orden de bytes:

private static final Charset[] UTF_ENCODINGS = { Charset.forName("UTF-8"), 
     Charset.forName("UTF-16LE"), Charset.forName("UTF-16BE") }; 

    private static Charset getEncoding(InputStream in) throws IOException { 
    charsetLoop: for (Charset encodings : UTF_ENCODINGS) { 
     byte[] bom = "\uFEFF".getBytes(encodings); 
     in.mark(bom.length); 
     for (byte b : bom) { 
     if ((0xFF & b) != in.read()) { 
      in.reset(); 
      continue charsetLoop; 
     } 
     } 
     return encodings; 
    } 
    return Charset.defaultCharset(); 
    } 

    private static String readText(File file) throws IOException { 
    Closer res = new Closer(); 
    try { 
     InputStream in = res.using(new FileInputStream(file)); 
     InputStream bin = res.using(new BufferedInputStream(in)); 
     Reader reader = res.using(new InputStreamReader(bin, getEncoding(bin))); 
     StringBuilder out = new StringBuilder(); 
     for (int ch = reader.read(); ch != -1; ch = reader.read()) 
     out.append((char) ch); 
     return out.toString(); 
    } finally { 
     res.close(); 
    } 
    } 

Uso :

public static void main(String[] args) throws IOException { 
    System.out.println(readText(new File("chinese.txt"))); 
    } 

(System.out utiliza la codificación predeterminada, por lo que si se imprime nada Sensi ble depende de su plataforma y configuration.)

+0

¡Está bien! De todas las respuestas que probé, ¡la tuya es la mejor! ¿Pero cómo leer unicode del archivo que acabo de guardar? Utilicé mi utilidad para leerlo y obtuve esto: 00ef 00bb 00bf 00e4 00b8 008a 00e6 00b5 00b7 Puedo ver desde el teclado de Word que hay dos caracteres chinos en él: Shang y Hai, pero ¿cómo puede Java leerlos de nuevo? Gracias ! – Frank

+0

Agregué un código que adivina mejor al leer archivos de texto arbitrarios. – McDowell

+0

¡Genial! ¡Eso es exactamente lo que estoy buscando! Deseo que esto sea parte del paquete Java de Sun, no es algo de lo que tengamos que preocuparnos. Gracias ! – Frank

4

Si se puede confiar que la codificación de caracteres por defecto es UTF-8 (o alguna otra codificación Unicode), puede utilizar el siguiente:

Writer w = new FileWriter("test.txt"); 
    w.append("上海"); 
    w.close(); 

La forma más segura es especificar siempre explícitamente la codificación:

Writer w = new OutputStreamWriter(new FileOutputStream("test.txt"), "UTF-8"); 
    w.append("上海"); 
    w.close(); 

P.S. Puede usar cualquier carácter Unicode en el código fuente de Java, incluso como nombre de método y variable, si el parámetro -encoding para javac está configurado correctamente. Eso hace que el código fuente sea más legible que el formulario escapado \uXXXX.

+0

Me gustaría, pero desde que uso Netbeans, después de cortar y pegar chino en un archivo java y guardarlo, no se mostrará (solo ver ?? ?) cuando vuelvo a abrir el archivo java en Netbeans. – Frank

+0

Quizás NetBeans está configurado para usar alguna codificación que no sea Unicode, o la fuente del editor no tiene todos los caracteres Unicode.No uso NetBeans, pero desde su archivo de ayuda veo que configuras la codificación en Project Properties | Fuentes | Codificación. –

+0

¿Está seguro de que el uso de qué codificación se guardó el archivo, si lo guardó utilizando algún otro editor? –

1

Aquí hay una manera entre muchas. Básicamente, sólo estamos especificando que la conversión se hará a UTF-8 bytes antes de dar salida a la FileOutputStream:

String FileName = "output.txt"; 

StringBuffer Shanghai_StrBuf=new StringBuffer("\u4E0A\u6D77"); 
boolean Append=true; 

Writer writer = new OutputStreamWriter(new FileOutputStream(FileName,Append), "UTF-8"); 
writer.write(Shanghai_StrBuf.toString(), 0, Shanghai_StrBuf.length()); 
writer.close(); 

que verifica manualmente esto en contra de las imágenes a http://www.fileformat.info/info/unicode/char/. En el futuro, siga los estándares de codificación de Java, incluidos los nombres de variables en minúsculas. Mejora la legibilidad.

1

Prueba de esto,

StringBuffer Shanghai_StrBuf=new StringBuffer("\u4E0A\u6D77"); 
    boolean Append=true; 

    Writer out = new BufferedWriter(new OutputStreamWriter(
     new FileOutputStream(FileName,Append), "UTF8")); 
    for (int i=0;i<Shanghai_StrBuf.length();i++) out.write(Shanghai_StrBuf.charAt(i)); 
    out.close(); 
3

tener mucho cuidado con los enfoques propuestos. Incluso especificando la codificación para el archivo de la siguiente manera:

Writer w = new OutputStreamWriter (new FileOutputStream ("test.txt"), "UTF-8");

no funcionará si está ejecutando bajo un sistema operativo como Windows. Incluso establecer la propiedad del sistema para file.encoding en UTF-8 no soluciona el problema. Esto se debe a que Java no puede escribir una marca de orden de bytes (BOM) para el archivo. Incluso si especifica la codificación al escribir en un archivo, abrir el mismo archivo en una aplicación como Wordpad mostrará el texto como basura porque no detecta la lista de materiales. Intenté ejecutar los ejemplos aquí en Windows (con una codificación de plataforma/contenedor de CP1252).

existe la siguiente fallo para describir el problema en Java:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058

La solución por el momento es escribir el orden de bytes etiquetarte para asegurar el archivo se abre correctamente en otras aplicaciones.Ver este para más detalles sobre la lista de materiales:

http://mindprod.com/jgloss/bom.html

y una solución más correcta ver el siguiente enlace:

http://tripoverit.blogspot.com/2007/04/javas-utf-8-and-unicode-writing-is.html

+0

¡Esperaba conseguir un camarón, ahora encontré un tiburón y un asesino de tiburones! Gracias. En la "solución correcta" que publicó, ¿por qué son "init();" líneas comentadas en Cerrar() y leer()? ¿Debo descomentarlos para que se ejecuten correctamente? – Frank

+0

No del todo seguro, pero no debería importar para fines de escritura, solo para leer. Si está leyendo un archivo UTF-8, debe omitir la lista de materiales, ya que confunde los problemas de Java, eso es lo que hace el método init. Puede valer la pena ponerse en contacto con el autor del blog para descubrir la razón detrás de esto. Lo siento, no puedo ser de más ayuda. – Jon

+0

Posiblemente podría descartar la parte de lectura del código. Parece que Apache ha tenido éxito en la creación de su propio BOMExclusionReader, consulte: https://issues.apache.org/jira/browse/IO-178 – Jon

Cuestiones relacionadas