2009-08-13 19 views
5

Estoy tratando de decodificar algunas cadenas UTF-8 en Java. Estas cadenas contienen algunos caracteres Unicode combinados, como CC 88 (combinación de diaresis). La secuencia de caracteres parece correcta, según http://www.fileformat.info/info/unicode/char/0308/index.htmJava UTF-8 comportamiento extraño

Pero la salida después de la conversión a Cadena no es válida. ¿Alguna idea?

byte[] utf8 = { 105, -52, -120 }; 
System.out.print("{{"); 
for(int i = 0; i < utf8.length; ++i) 
{ 
    int value = utf8[i] & 0xFF; 
    System.out.print(Integer.toHexString(value)); 
} 
System.out.println("}}"); 
System.out.println(">" + new String(utf8, "UTF-8")); 

Salida:

 
    {{69cc88}} 
    >i? 

Respuesta

9

La consola, que está a la salida (por ejemplo, ventanas) puede no ser compatible con Unicode, y puede destrozar los personajes. La salida de la consola no es una buena representación de los datos.

Intente escribir la salida en un archivo, asegurándose de que la codificación sea correcta en el FileWriter, luego abra el archivo en un editor compatible con Unicode.

Como alternativa, use un depurador para asegurarse de que los caracteres sean los esperados. Simplemente no confíes en la consola.

+0

+1: en Ubuntu 9.04 en un terminal (gnome-terminal) la salida es la i con diaresis como probablemente se espera. –

+0

Me gusta esta palabra "diaéresis". Es posible que deba usarlo más a menudo en una conversación. – skaffman

+1

:) intenta también "umlaut", y serás el hombre de la noche. –

4

El código está bien, pero como dijo skaffman su consola probablemente no admite el caracter apropiado.

Para probar a ciencia cierta, es necesario imprimir los valores Unicode del carácter:

public class Test { 
    public static void main(String[] args) throws Exception { 
     byte[] utf8 = { 105, -52, -120 }; 
     String text = new String(utf8, "UTF-8"); 
     for (int i=0; i < text.length(); i++) { 
      System.out.println(Integer.toHexString(text.charAt(i))); 
     } 
    } 
} 

Esto imprime 69, 308 - que es correct (U + 0069, U + 0308).

4

Ambos tienen razón. Gracias !!

Aquí cómo finalmente resuelto el problema, en Eclipse en Windows:

  • En la configuración Ejecutar, pestaña Argumentos, que añade "-Dfile.encoding=UTF-8" a los argumentos de VM
  • En la configuración Ejecutar, ficha Común , me puse la codificación UTF consola a-8

Y he modificado el código de la siguiente manera:

byte[] utf8 = { 105, -52, -120 }; 
System.out.print("{{"); 
for(int i = 0; i < utf8.length; ++i) 
{ 
    int value = utf8[i] & 0xFF; 
    System.out.print(Integer.toHexString(value)); 
} 
System.out.println("}}"); 

PrintStream sysout = new PrintStream(System.out, true, "UTF-8"); 
sysout.print(">" + new String(utf8, "UTF-8")); 

Salida:

 
{{69cc88}} 
> ï 

Gracias!

+0

No debería necesitar el modificador "-Dfile.encoding = UTF-8" si va a codificar los datos usted mismo con PrintStream. (Configurar manualmente la propiedad "file.encoding" puede ser problemático para cualquier código que necesite conocer la codificación del sistema). – McDowell

1

Java, no sin razón, codifica caracteres Unicode en bytes codificados en el sistema nativo antes de escribirlos en stdout. Algunos sistemas operativos, como muchas distribuciones de Linux, usan UTF-8 como su conjunto de caracteres predeterminado, lo cual es bueno.

Las cosas son un poco diferentes en Windows por una variedad de razones de compatibilidad con versiones anteriores. La codificación predeterminada del sistema será una de las páginas de códigos "ANSI" y si abre el símbolo del sistema predeterminado (cmd.exe) será una de las antiguas páginas de códigos "OEM" de DOS (aunque es posible obtener ANSI y Unicode allí with a bit of work)

Dado que U + 0308 no está en ninguno de los juegos de caracteres "ANSI" (probablemente 1252 en su caso), se codificará como un carácter de error (generalmente un signo de interrogación).

Una alternativa a Unicode que permite que todo está a normalize la secuencia de la combinación de U + 0069 U + 0308 con el único carácter U + 00EF:

public static void emit(String foo) throws IOException { 
    System.out.println("Literal: " + foo); 
    System.out.print("Hex: "); 
    for (char ch : foo.toCharArray()) { 
     System.out.print(Integer.toHexString(ch & 0xFFFF) + " "); 
    } 
    System.out.println(); 
    } 

    public static void main(String[] args) throws IOException { 
    String foo = "\u0069\u0308"; 
    emit(foo); 
    foo = Normalizer.normalize(foo, Normalizer.Form.NFC); 
    emit(foo); 
    } 

Bajo windows-1252, este código emitirá:

 
Literal: i? 
Hex: 69 308 
Literal: ï 
Hex: ef