¿Cómo puedo abrir archivos que contienen acentos en Java?

( edición para aclaración y agregar algún código )

Hola, tenemos un requisito para analizar los datos enviados por usuarios de 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 progtwig Java, Java ordena los nombres y lanza una excepción de “archivo no encontrado” para tratar de abrirlos.

Este sencillo progtwig puede ilustrar el problema:

 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 progtwig en un directorio que contiene el archivo special_á_ã_è_characters.doc y la configuración predeterminada en inglés de EE. UU. Da:

special_�_�_�_characters.doc

Configuración del idioma mediante 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 dentro del progtwig como el siguiente tampoco tiene ningún efecto. A continuación he modificado el progtwig para a) intentar abrir el archivo yb) imprimir el nombre tanto en ASCII como en una matriz de bytes cuando no se 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, la pantalla de la consola funciona incluso cuando se usa LANG = en_US.UTF-8 para algunas utilidades como el eco de bash:

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

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

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

Segundo, el � es tÃpico para el caracter de reemplazo Unicode 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")); // � 

Entonces hay dos problemas:

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

Para una JVM de Sun, el argumento 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(); 

Eso debería haber sido lo siguiente para excluir la influencia de la encoding predeterminada de la plataforma:

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

Si eso todavía muestra lo mismo, entonces el problema es más profundo. ¿Qué JVM exactamente estás usando? Haz una 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.

Es un error en JRE / JDK que existe desde hace años.

¿Cómo arreglar java cuando se negó a abrir un archivo con charater especial en nombre de archivo?

File.exists () falla con caracteres Unicode en el nombre

Ahora estoy volviendo a enviarles un nuevo informe de fallas ya que LC_ALL = en_us arreglará algunos casos, mientras tanto fallará en otros casos.

Es un error en la aplicación del archivo java del viejo skool, ¿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 …

La propiedad del sistema Java file.encoding debe coincidir con la encoding de caracteres de la consola. La propiedad debe establecerse al iniciar java en la línea de comandos:

 java -Dfile.encoding=UTF-8 … 

Normalmente, esto sucede automáticamente, porque la encoding de la consola suele ser la encoding predeterminada de la plataforma, y ​​Java utilizará la encoding predeterminada de la plataforma si no especifica una explícitamente.

¡Bien, fui estrangulado con este problema todo el día! Mi código anterior (erróneo) era el mismo que tú:

 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 yo no puede cambiar esto sin el riesgo de que Zimbra deje de funcionar …)

Así que decidí usar las nuevas clases del paquete java.nio.file (Archivos y Rutas):

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

Por lo tanto, si está utilizando Java 1.7, debe probar las nuevas clases en el paquete java.nio.file: ¡me salvó el día!

Espero eso ayude

En el uso de DirectoryStream, no olvide cerrar la secuencia (try-with-resources puede ayudar aquí)