2010-06-18 14 views
12

(edición de aclaración y adición de un código)¿Cómo puedo abrir archivos que contienen acentos en Java?

Hola, Tenemos un requisito para analizar los datos enviados por los usuarios en todo el mundo. Nuestros sistemas Linux tienen una configuración regional predeterminada en_US.UTF-8. Sin embargo, a menudo recibimos archivos con marcas diacríticas en sus nombres como "special_á_ã_è_characters.doc". Aunque el sistema operativo puede tratar bien estos archivos, y una cadena muestra que el sistema operativo transfiere el nombre de archivo correcto al programa Java, Java ordena los nombres y lanza una excepción de "archivo no encontrado" para tratar de abrirlos.

Este sencillo programa puede ilustrar el tema:

import java.io.*; 
import java.text.*; 

public class load_i18n 
{ 
    public static void main(String [] args) { 
    File actual = new File("."); 
    for(File f : actual.listFiles()){ 
     System.out.println(f.getName()); 
    } 
    } 
} 

La ejecución de este programa en un directorio que contiene el archivo special_á_ã_è_characters.doc y el valor predeterminado de EE.UU. Inglés locale da:

special_�_�_�_characters.doc

Configuración del idioma a través de la exportación LANG = es_ES @ UTF-8 imprime el nombre de archivo correctamente (pero es una solución inaceptable ya que todo el sistema se está ejecutando en español). Establecer explícitamente la configuración regional el programa como el siguiente tampoco tiene ningún efecto. A continuación he modificado el programa para a) intente abrir el archivo y b) imprimir el nombre en ASCII y como una matriz de bytes cuando no puede abrir el archivo:

import java.io.*; 
import java.util.Locale; 
import java.text.*; 

public class load_i18n 
{ 
    public static void main(String [] args) { 
    // Stream to read file 
    FileInputStream fin; 

    Locale locale = new Locale("es", "ES"); 
    Locale.setDefault(locale); 
    File actual = new File("."); 
    System.out.println(Locale.getDefault()); 
    for(File f : actual.listFiles()){ 
     try { 
     fin = new FileInputStream (f.getName()); 
     } 
     catch (IOException e){ 
     System.err.println ("Can't open the file " + f.getName() + ". Printing as byte array."); 
     byte[] textArray = f.getName().getBytes(); 
     for(byte b: textArray){ 
      System.err.print(b + " "); 
     } 
     System.err.println(); 
     System.exit(-1); 
     } 

     System.out.println(f.getName()); 
    } 
    } 
} 

Esto produce la salida

es_ES 
load_i18n.class 
Can't open the file special_�_�_�_characters.doc. Printing as byte array. 
115 112 101 99 105 97 108 95 -17 -65 -67 95 -17 -65 -67 95 -17 -65 -67 95 99 104 97 114 97 99 116 101 114 115 46 100 111 99 

Esto muestra que el problema NO es solo un problema con la visualización de la consola, ya que los mismos caracteres y sus representaciones se envían en bytes o en formato ASCII. De hecho, pantalla de la consola funciona incluso cuando se utiliza LANG = en_US.UTF-8 para algunas utilidades como eco de fiesta:

[[email protected] tmp]$ echo $LANG 
en_US.UTF-8 
[[email protected] tmp]$ echo * 
load_i18n.class special_á_ã_è_characters.doc 
[[email protected] tmp]$ ls 
load_i18n.class special_?_?_?_characters.doc 
[[email protected] tmp]$ 

¿Es posible modificar este código de tal manera que cuando se ejecuta en Linux con LANG = en_US.UTF-8, lee el nombre del archivo de tal manera que se puede abrir con éxito?

+7

Su ejemplo no muestra que intente abrir esos archivos, solo imprima el nombre.Si Java puede abrir el archivo y si su consola de salida estándar (que no tiene nada que ver con Java) puede representar correctamente los caracteres, son dos cosas muy diferentes. Muéstranos el código que dio la IOException y da los detalles de IOException y stacktrace. –

+0

Echa un vistazo a las respuestas que recomiendan el uso de las propiedades del sistema Java (user.language, user.country, user.variant) aquí: http://stackoverflow.com/questions/64038/setting-java-locale-settings –

+0

Lo siento - I nunca llegar al punto de abrir el archivo. Una llamada a, digamos FileInputStream fallaría porque no puedo pasarle el nombre correcto del archivo. El archivo "special_�_�_�_characters.doc" no existe. El archivo "special_á_ã_è_characters.doc" sí lo hace, pero mi iteración de directorios nunca lo menciona. –

Respuesta

1

La propiedad del sistema Java file.encoding debe coincidir con la codificación de caracteres de la consola. La propiedad se debe establecer cuando se inicia java en la línea de comandos:

java -Dfile.encoding=UTF-8 … 

Normalmente esto ocurre de forma automática, ya que la codificación de la consola suele ser la plataforma de codificación por defecto, y Java utilizará la codificación predeterminada plataforma si no lo hace especifica uno explícitamente

+1

file.encoding es para el contenido del archivo, no el nombre del archivo – Martin

7

En primer lugar, la codificación de caracteres utilizada no está directamente relacionada con la configuración regional. Así que cambiar la configuración regional no ayudará mucho.

En segundo lugar, el � es típico para el Unicode replacement character U+FFFD impreso en ISO-8859-1 en lugar de UTF-8. Aquí hay una evidencia:

System.out.println(new String("�".getBytes("UTF-8"), "ISO-8859-1")); // � 

Así que hay dos problemas:

  1. Su JVM está leyendo esos caracteres especiales como .
  2. Su consola está utilizando ISO-8859-1 para mostrar los caracteres.

Para una JVM de Sun, el argumento de VM -Dfile.encoding=UTF-8 debería solucionar el primer problema. El segundo problema se debe solucionar en la configuración de la consola. Si está utilizando, por ejemplo, Eclipse, puede cambiarlo en Ventana> Preferencias> General> Espacio de trabajo> Codificación de archivo de texto. Configúrelo en UTF-8 también.


actualización: Según su actualización

byte[] textArray = f.getName().getBytes(); 

que debería haber sido el siguiente excluir la influencia de codificación predeterminado de la plataforma:

byte[] textArray = f.getName().getBytes("UTF-8"); 

Si eso sigue mostrando la misma , entonces el problema es más profundo. ¿Qué JVM exactamente estás usando? Haz un java -version. Como se dijo anteriormente, el argumento -Dfile.encoding es específico de Sun JVM. Algunas máquinas Linux se envían con JVM de GNU o JVM de OpenJDK y este argumento puede no funcionar.

+1

Lo intenté y no funcionó. java -Dfile.encoding = UTF-8 load_i18n es_ES special_�_�_�_characters.doc probablemente estoy equivocado, pero no estoy convencido de que hay un problema de la consola todavía. Redirijo la salida a un archivo para que no haya consola involucrada y sigo obteniendo los mismos resultados. Hago un "od -a" en el archivo y aquí está la salida relevante: 0000200 e f i l e nl s p e c i a l _ o? 0000220 = _ o? = _ o? = _ c h a r a c 0000240 t s r s. d o c nl r e a d _ i 1 –

+0

En cuanto al primer problema: puede ser específico de la plataforma/JVM. Difícil de decir a partir de ahora. En cuanto al segundo problema: ¿el archivo está escrito con un 'OutputStreamWriter' utilizando UTF-8 y se ve con un visor compatible con UTF-8? – BalusC

+0

@Mark, no estoy seguro de por qué está pasando el nombre de archivo 'destrozado' en la línea de comando. El flujo parece ser (1) Java obtiene el nombre de archivo correcto del sistema operativo (2) Java escribe el nombre de archivo en stdout, donde se arruina (3) toma el nombre de archivo dañado y lo vuelve a pasar a una herramienta diferente (4) Java Hands the nombre de archivo dañado en el sistema operativo, que no puede encontrar el archivo. Solución (2), y el problema desaparece; pasar el nombre de archivo MANGLED en (3) solo empeora las cosas. – Cowan

1

Bueno, ¡estuve estrangulado con este problema todo el día! Mi (mal) código anterior era el mismo que usted:

for(File f : dir.listFiles()) { 
String filename = f.getName(); // The filename here is wrong ! 
FileInputStream fis = new FileInputStream (filename); 
} 

y no funciona (estoy usando Java 1.7 Oracle en CentOS 6, LANG y LC_CTYPE = fr_FR.UTF-8 para todos los usuarios excepto zimbra => LANG y LC_CTYPE = C - que por cierto es la causa de este problema, pero no puedo cambiar esto sin el riesgo de que Zimbra deje de funcionar ...)

Así que decidí usar las nuevas clases de Java Paquete .nio.file (Archivos y rutas):

DirectoryStream<Path> paths = Files.newDirectoryStream(Paths.get(outputName)); 
for (Iterator<Path> iterator = paths.iterator(); iterator.hasNext();) { 
    Path path = iterator.next(); 
    String filename = path.getFileName().toString(); // The filename here is correct 
    ... 
} 

Si está utilizando Java 1.7 , deberías probar nuevas clases en el paquete java.nio.file: ¡me salvó el día!

creo que sirve

0

En el uso DirectoryStream entonces no se olvide de cerrar la secuencia (Try-con-recursos puede ayudar aquí)

2

Se trata de un error en la vieja escuela de Java API de archivos, tal vez solo en un mac? De todos modos, la nueva API java.nio funciona mucho mejor. Tengo varios archivos que contienen caracteres Unicode que no se pudieron cargar usando clases java.io ... Después de convertir todo mi código para usar java.nio.Path TODO comenzó a funcionar. Y reemplacé Apache FileUtils (que tiene el mismo problema) con java.nio.Files ...

+0

Esto funcionó para mí. La respuesta aceptada no sirvió para mi caso. –

Cuestiones relacionadas