2011-01-24 18 views
10

Cómo convertir texto de UTF-8/CP1251 (ventanas cirílico) a DOS cirílico (CP866)Java juego de caracteres de codificación problema (de UTF8 a CP866)

Me parece que este ejemplo:

Charset fromCharset = Charset.forName("utf8"); 
Charset toCharset = Charset.forName("cp866"); 

String text1 = "Николай"; // my name in bulgarian 
String text2 = "Nikolay"; // my name in english 

System.out.println("TEXT1 :[" + toCharset.decode(fromCharset.encode(text1)).toString() + "]"); 
System.out.println("TEXT2 :[" + toCharset.decode(fromCharset.encode(text2)).toString() + "]"); 

Y el la entrada es:

TEXT1 :[╨Э╨╕╨║╨╛╨╗╨░╨╣] // WRONG 
TEXT2 :[Nikolay] // CORRECT 

¿Dónde está el problema?

+2

¿Qué espera? Está codificando "Николай" usando UTF-8, luego decodificando los bytes codificados usando Cp866. La salida me parece razonable, pero obviamente esperas que ocurra alguna otra magia. – jarnbjo

Respuesta

5

El problema es que está intentando decodificar la salida de una codificación como si fuera una diferente.

Imagina que tienes un programa que solo puede escribir archivos JPEG, y otro que solo puede leer archivos PNG ... ¿esperarías poder leer el resultado del primer programa con el segundo?

En este caso, las dos codificaciones son compatibles para caracteres ASCII, pero fundamentalmente estás haciendo lo incorrecto.

Si tiene texto que ya está en UTF-8, debe leer que a partir de datos binarios en una cadena Unicode utilizando la codificación UTF-8, y luego escribirlo con la otra codificación a datos binarios de nuevo. Unicode es básicamente el paso intermedio, como el formato de texto nativo de Java. Esto sería equivalente a cargar la salida JPEG en otro programa que podría realizar la conversión a PNG antes de leerlo con la segunda aplicación.

13

Primero: si tienes un objeto String, entonces ya no tiene una codificación, ¡es una cadena Unicode pura (*)!

En Java, se utilizan codificaciones sólo se al convertir de bytes (byte[]) a una cadena (String) o viceversa. (En teoría, podría hacer una conversión directa de byte[] a byte[] pero aún no lo he visto en Java).

Si tiene algunos datos CP1251 codificado, entonces debe ser un byte[] (es decir, una matriz de bytes) o en algún tipo de corriente (por ejemplo proporcionado a usted como un InputStream).

Si desea proporcionar algunos datos como CP866, a continuación, usted debe proporcionar ya sea como un byte[] o como una especie de corriente (por ejemplo, un `OutputStream).

Además, no existe la frase "utf8/cp1251". UTF-8 y CP-1251 son codificaciones de caracteres no relacionadas. Su entrada es UTF-8 o CP-1251 (o algo más). Realmente no puede ser ambos (+).

Y aquí está el vínculo obligatorio: The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

(*) sí, en sentido estricto tiene una codificación y es UTF-16, pero para la mayoría de los propósitos que puede (y debe) pensar en él como un "encodingless ideales cadena Unicode"
(+) en sentido estricto, que podría ser tanto si sólo se está utilizando caracteres que codifican para los mismos bytes en ambas codificaciones, que suele ser el subconjunto ASCII

+0

¿Por qué agregar el "suspiro ..." allí? No agrega nada a la respuesta y, a primera vista, parece que menosprecia el OP. – McStretch

+0

Muy buena explicación de varios malentendidos comunes codificaciones de caracteres wrt. Ojalá pudiera votar más de una vez. – sleske

+0

@McStretch: tienes razón. Es solo mi respuesta inicial cuando leo preguntas como esa. Solo agregué el resto de la respuesta porque responder con * solo * "suspiro" sería realmente desagradable ;-) –

0

el problema es, a su salida de la consola no es cp866. La consola es una, la conversión es otra.

Internamente La cadena en java siempre es unicode, el juego de caracteres es importante para las operaciones de entrada/salida. No ha especificado qué quiere hacer con la cadena 'convertida', pero definitivamente debería ver las clases InputStreamReader/OutputStreamWriter. Proporcionan configuración de conjunto de caracteres para sus operaciones de E/S.

+3

Eso podría ser un problema adicional, pero el problema básico es que está encadenando operaciones que no tienen sentido juntas. Codificar algo de texto como UTF-8 y decodificarlo usando otra codificación no produce un resultado útil. –

+0

Quiero enviar datos a la impresora fiscal que funciona con Cyrillic para DOS – NikolayGS

+2

Por lo tanto, el OutputStreamWriter es lo que necesita. Incluso si necesita byte [] para enviar directamente al puerto, puede beneficiarse de Writer escribiendo datos en ByteArrayOutputStream. –

3

corta:

a descifrar una serie UTF8 como CP866. Como utf8 y cp866 solo comparten símbolos ASCII, todo lo demás se destroza.

larga:

Java representa cadenas utilizando UTF-16 internamente, todos los objetos String son codificados en UTF-16.

Charset.encode() crea un bytebuffer que contiene el String en la codificación elegida, en su código esto convierte el Java UTF-16 String en un utf-8 codificado byte-array.

Charset.decode() toma un bytebuffer codificado como Charset y lo convierte en un Java UTF-16 String. En su caso usted decodifica una cadena utf-8 con un decodificador cp866, lo que resulta en una cadena destrozada.

Dado que las cadenas java tienen una codificación especificada, debe especificarla cuando las lea o las escriba. Tanto InputStreamReader como OutputStreamWriter proporcionan ctors con un argumento Charset.

Aquí hay un ejemplo de cómo puede convertir archivos/secuencias.

//input the source is encoded in fromCharset 
BufferedReader in = new BufferedReader(new InputStreamReader(...,fromCharset)); 
//output the target will be encoded in toCharset 
PrintWriter out = new PrintWriter(new OutputStreamWriter(...,toCharset)); 
//reads a decoded String 
String line = in.readLine(); 
while(line != null) 
{ 
    out.println(line); 
    line = in.readLine(); 
} 
4

corto resolver su problema:

System.out.write("ВАСЯ\n".getBytes("cp866")); // its right 
System.out.println("ВАСЯ".getBytes("cp866")); // its wrong 

resultado de cmd.exe:

C: \ Documents and Settings \ afram \ Мои документы \ NetBeansProjects \ Codificación \ dist> java -jar Encoding.jar

ВАСЯ

[B @ 1bab50a

+0

'System.out.write (" ВАСЯ \ n ".getBytes (" cp866 "))' es incorrecto. La salida es 'Р? Р? РЎРЇ' – Green

+1

@Green que es probable porque la codificación de su terminal es diferente de cp866. – vadipp

Cuestiones relacionadas