2009-01-31 25 views
6

Tengo un archivo codificado como iso-8859-1 y contiene caracteres como ô.Aplicación Java: no se puede leer el archivo codificado iso-8859-1 correctamente

Estoy leyendo este archivo con el código de Java, algo así como:

File in = new File("myfile.csv"); 
InputStream fr = new FileInputStream(in); 
byte[] buffer = new byte[4096]; 
while (true) { 
    int byteCount = fr.read(buffer, 0, buffer.length); 
    if (byteCount <= 0) { 
     break; 
    } 

    String s = new String(buffer, 0, byteCount,"ISO-8859-1"); 
    System.out.println(s); 
} 

Sin embargo, el carácter o ¿siempre confusa, por lo general el estampado a modo? .

He leído sobre el tema (y aprendí un poco en el camino) p.

pero todavía no puede conseguir este funcionamiento

Curiosamente esto funciona en mi PC local (XP), pero no en mi máquina Linux.

He comprobado que mi JDK compatible con los juegos de caracteres necesarios (que son estándar, por lo que esto no es una sorpresa) usando:

System.out.println(java.nio.charset.Charset.availableCharsets()); 
+0

Debo agregar que puedo ver los caracteres o el archivo original correctamente usando mi terminal linux si simplemente cat el contenido del archivo – Joel

+0

¿Qué codificación de caracteres está utilizando su terminal? – McDowell

+0

Curiosamente, si agrego la propiedad java en tiempo de ejecución "-Dfile.encoding = UTF16" funciona como se esperaba, aunque no veo por qué esto debería importar, y no lo veo como una solución, sino más como un hack. No funciona con la propiedad establecida en UTF8. – Joel

Respuesta

12

Sospecho que su archivo no es en realidad codificado como ISO-8859-1, o System.out no sabe cómo imprimir el carácter.

Recomiendo que para verificar el primero, examine el byte relevante en el archivo. Para comprobar si el segundo, examinar el carácter relevante en la cadena, que se puede imprimir con

System.out.println((int) s.getCharAt(index)); 

En ambos casos el resultado debe ser 244 decimal; 0xf4 hex.

Consulte my article on Unicode debugging para obtener información general (el código presentado está en C#, pero es fácil de convertir a Java, y los principios son los mismos).

En general, por cierto, envolvería la transmisión con un InputStreamReader con la codificación correcta; es más fácil que crear nuevas cadenas "a mano". Me doy cuenta de que esto puede ser solo un código de demostración.

EDIT: He aquí una manera muy fácil de demostrar si es o no la consola funcionará:

System.out.println("Here's the character: \u00f4"); 
+0

han utilizado la herramienta de archivo de Linux para probar el tipo del archivo: archivo --mime FranceJ2.csv FranceJ2.csv: text/plain; charset = iso-8859-1 y también confirmó que puedo leerlo correctamente, por ejemplo vi pero seguiré sus sugerencias. – Joel

+1

No confíes en las herramientas que intentan detectar codificaciones de caracteres automáticamente. Siempre están basados ​​en heurística, y tienen que serlo. No saben qué texto debe contener su archivo. –

+0

Un hexdump del archivo produce: 0000000 0df4 000a (¿alguna sugerencia ?!) – Joel

3

Si puede, trate de ejecutar el programa en el depurador para ver lo que hay dentro de su cadena 's' después de que se crea. Es posible que tenga contenido correcto, pero la salida se distorsiona después de la llamada a System.out.println (s). En ese caso, probablemente exista una discrepancia entre lo que Java piensa que es la codificación de su salida y la codificación de caracteres de su terminal/consola en Linux.

9

analizar el archivo como bloques de tamaño fijo de bytes no es bueno --- ¿y si algún personaje tiene una representación de bytes que se extiende a través de dos bloques? Utilice un InputStreamReader con la codificación de caracteres apropiado en su lugar:

BufferedReader br = new BufferedReader(
     new InputStreamReader(
     new FileInputStream("myfile.csv"), "ISO-8859-1"); 

char[] buffer = new char[4096]; // character (not byte) buffer 

while (true) 
{ 
     int charCount = br.read(buffer, 0, buffer.length); 

     if (charCount == -1) break; // reached end-of-stream 

     String s = String.valueOf(buffer, 0, charCount); 
     // alternatively, we can append to a StringBuilder 

     System.out.println(s); 
} 

Por cierto, no olvide comprobar que el carácter Unicode de hecho se puede mostrar correctamente.También puede redirigir la salida del programa a un archivo y luego compararlo con el archivo original.

Como Jon Skeet sugiere, el problema también puede estar relacionado con la consola. Pruebe System.console().printf(s) para ver si hay alguna diferencia.

1

Básicamente, si funciona en su PC XP local pero no en Linux, y está analizando el mismo archivo (es decir, lo transfirió de forma binaria entre las cajas), entonces probablemente tenga algo que ver con el Llamada System.out.println. No sé cómo verificar la salida, pero si lo haces conectándose con un shell remoto desde el cuadro de XP, entonces hay que considerar el conjunto de caracteres del shell (y del cliente). También, lo que Zach Scrivena sugiere también es cierto: no se puede asumir que se pueden crear cadenas a partir de fragmentos de datos de esa manera: use un InputStreamReader o lea los datos completos en una matriz primero (obviamente no va a funcionar para un archivo grande). Sin embargo, dado que parece funcionar en XP, me atrevería a suponer que probablemente este no sea su problema en este caso específico.

6

@Joel - your own answer confirma que el problema es una diferencia entre la codificación predeterminada en su sistema operativo (UTF-8, la que Java ha recogido) y la codificación que está usando su terminal (ISO-8859-1).

consideran este código:

public static void main(String[] args) throws IOException { 
    byte[] data = { (byte) 0xF4 }; 
    String decoded = new String(data, "ISO-8859-1"); 
    if (!"\u00f4".equals(decoded)) { 
     throw new IllegalStateException(); 
    } 

    // write default charset 
    System.out.println(Charset.defaultCharset()); 

    // dump bytes to stdout 
    System.out.write(data); 

    // will encode to default charset when converting to bytes 
    System.out.println(decoded); 
} 

Por defecto, en mi Ubuntu (8.04) terminal utiliza la codificación UTF-8. Con esta codificación, esto se imprime:?

UTF-8
& # x00F4;

Si cambio de codificación de la terminal de la norma ISO 8859-1, este se imprime:

UTF-8
& # x00F4; & # x00C3; & # x00B4;

En ambos casos, los mismos bytes están siendo emitidas por el programa Java:

5554 462d 380a f4c3 b40a 

La única diferencia está en cómo el terminal interpreta los bytes que recibe. En ISO 8859-1, & # x00F4; está codificado como 0xF4. En UTF-8, & # x00F4; está codificado como 0xC3B4. Los otros caracteres son comunes a ambas codificaciones.

+0

Me falta algo aquí, ¿qué es el volcado '5554 462d 380a f4c3 b40a'? Ciertamente no la llamada 'System.out.write (data)'? –

+1

@Mr_and_Mrs_D Estos son los bytes que JRE escribe en el dispositivo (STDOUT) con las tres llamadas a 'System.out'. Los bytes '0A' marcan las nuevas líneas escritas por' println'. _Había una respuesta escrita por el autor de la pregunta, ya que se eliminó, pero no creo que poder leerla agrega mucho._ – McDowell

+0

Gracias por hacer un seguimiento, entendí que hubo una respuesta del autor desde que se eliminó, no puedo leerla - Gracias :) –

Cuestiones relacionadas