Java: lectores y codificaciones

La encoding predeterminada de Java es ASCII . ¿Sí? (Ver mi edición a continuación)

Cuando un archivo de texto está codificado en UTF-8 ? ¿Cómo sabe un lector que tiene que usar UTF-8 ?

Los lectores de los que hablo son:

  • FileReader s
  • BufferedReader s de Socket s
  • Un Scanner de System.in

EDITAR

Resulta que nuestra encoding depende del sistema operativo, lo que significa que lo siguiente no es cierto en todos los sistemas operativos:

 'a'== 97 

¿Cómo sabe un lector que tiene que usar UTF-8?

Normalmente usted mismo lo especifica en un InputStreamReader . Tiene un constructor tomando la encoding de caracteres. P.ej

 Reader reader = new InputStreamReader(new FileInputStream("c:/foo.txt"), "UTF-8"); 

Todos los demás lectores (hasta donde yo sé) usan la encoding de caracteres predeterminada de la plataforma, que de hecho no puede ser la encoding correcta (como -cough- CP-1252 ).

En teoría, también puede detectar la encoding de caracteres automáticamente en función de la marca de orden de bytes . Esto distingue varias codificaciones unicode de otras codificaciones. Desafortunadamente, Java SE no tiene ninguna API para esto, pero puedes crear uno que se pueda utilizar para reemplazar a InputStreamReader como en el ejemplo anterior:

 public class UnicodeReader extends Reader { private static final int BOM_SIZE = 4; private final InputStreamReader reader; /** * Construct UnicodeReader * @param in Input stream. * @param defaultEncoding Default encoding to be used if BOM is not found, * or null to use system default encoding. * @throws IOException If an I/O error occurs. */ public UnicodeReader(InputStream in, String defaultEncoding) throws IOException { byte bom[] = new byte[BOM_SIZE]; String encoding; int unread; PushbackInputStream pushbackStream = new PushbackInputStream(in, BOM_SIZE); int n = pushbackStream.read(bom, 0, bom.length); // Read ahead four bytes and check for BOM marks. if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) { encoding = "UTF-8"; unread = n - 3; } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) { encoding = "UTF-16BE"; unread = n - 2; } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) { encoding = "UTF-16LE"; unread = n - 2; } else if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00) && (bom[2] == (byte) 0xFE) && (bom[3] == (byte) 0xFF)) { encoding = "UTF-32BE"; unread = n - 4; } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) && (bom[2] == (byte) 0x00) && (bom[3] == (byte) 0x00)) { encoding = "UTF-32LE"; unread = n - 4; } else { encoding = defaultEncoding; unread = n; } // Unread bytes if necessary and skip BOM marks. if (unread > 0) { pushbackStream.unread(bom, (n - unread), unread); } else if (unread < -1) { pushbackStream.unread(bom, 0, 0); } // Use given encoding. if (encoding == null) { reader = new InputStreamReader(pushbackStream); } else { reader = new InputStreamReader(pushbackStream, encoding); } } public String getEncoding() { return reader.getEncoding(); } public int read(char[] cbuf, int off, int len) throws IOException { return reader.read(cbuf, off, len); } public void close() throws IOException { reader.close(); } } 

Editar como respuesta en tu edición:

Así que la encoding depende del sistema operativo. Entonces eso significa que no en todos los sistemas operativos esto es cierto:

 'a'== 97 

No, esto no es cierto. La encoding ASCII (que contiene 128 caracteres, 0x00 hasta con 0x7F ) es la base de todas las demás codificaciones de caracteres. Solo los caracteres que se encuentran fuera del ASCII caracteres ASCII pueden correr el riesgo de mostrarse de manera diferente en otra encoding. Las codificaciones ISO-8859 cubren los caracteres en el rango ASCII con los mismos puntos de código. Las codificaciones Unicode cubren los caracteres en el rango ISO-8859-1 con los mismos puntos de código.

Puede encontrar en cada uno de esos blogs una lectura interesante:

  1. El mínimo absoluto, cada desarrollador de software Absolutamente, definitivamente, debe saber sobre Unicode y conjuntos de caracteres (¡Sin excusas!) (Más teóricos de los dos)
  2. Unicode - Cómo obtener los personajes correctos? (más práctico de los dos)

La encoding predeterminada de Java depende de su sistema operativo. Para Windows, normalmente es “windows-1252”, para Unix suele ser “ISO-8859-1” o “UTF-8”.

Un lector conoce la encoding correcta porque le dices la encoding correcta. Desafortunadamente, no todos los lectores te permiten hacer esto (por ejemplo, FileReader no lo hace), por lo que a menudo tienes que usar un InputStreamReader .

Para la mayoría de los lectores, Java usa el conjunto de caracteres y encoding que tenga su plataforma: esto puede ser cierto de ASCII o UTF-8, o algo más exótico como JIS (en Japón). Los caracteres de este conjunto se convierten luego en el UTF-16 que Java utiliza internamente.

Hay una solución alternativa si la encoding de la plataforma es diferente a la encoding de un archivo (mi problema: los archivos UTF-8 son estándar, pero mi plataforma usa la encoding Windows-1252). Cree una instancia de InputStreamReader que use el constructor que especifica la encoding.

Edición: haz esto así:

 InputStreamReader myReader = new InputStreamReader(new FileInputStream(myFile),"UTF-8"); //read data myReader.close(); 

Sin embargo, en el IIRC hay algunas disposiciones para la autodetección de codificaciones comunes (como UTF-8 y UTF-16). UTF-16 puede detectarse mediante la marca de orden de bytes al principio. UTF-8 también sigue ciertas reglas, pero en general la diferencia b / w de su plataforma de encoding y UTF-8 no va a importar a menos que esté usando caracteres internacionales en lugar de los latinos.

Me gustaría abordar esta parte primero:

La encoding predeterminada de Java es ASCII. ¿Sí?

Hay al menos 4 cosas diferentes en el entorno de Java que se podría llamar “encoding predeterminada”:

  1. el “conjunto de caracteres predeterminado” es lo que Java utiliza para convertir bytes en caracteres (y byte[] en String ) en Runtime, cuando no se especifica nada más. Esto depende de la plataforma, la configuración, los argumentos de la línea de comandos, … y generalmente es solo la encoding predeterminada de la plataforma.
  2. la encoding de caracteres internos que Java usa en valores char y objetos String . ¡Este es siempre UTF-16 ! No hay forma de cambiarlo, ¡solo es UTF-16! Esto significa que un char representa a siempre tiene el valor numérico 97 y un char que representa π siempre tiene el valor numérico 960.
  3. la encoding de caracteres que Java usa para almacenar constantes de cadena en archivos .class . Este es siempre UTF-8. No hay forma de cambiarlo.
  4. el conjunto de caracteres que el comstackdor de Java utiliza para interpretar el código fuente de Java en los archivos .java . Este predeterminado es el juego de caracteres predeterminado, pero se puede configurar en tiempo de comstackción.

¿Cómo sabe un lector que tiene que usar UTF-8?

No es así Si tiene algún archivo de texto plano, debe conocer la encoding para leerlo correctamente. Si tienes suerte, puedes adivinar (por ejemplo, puedes probar la encoding predeterminada de la plataforma), pero ese es un proceso propenso a errores y en muchos casos ni siquiera tendrías una forma de darte cuenta de que te equivocaste. Esto no es específico de Java. Es cierto para todos los sistemas.

Algunos formatos como XML y todos los formatos basados ​​en XML se diseñaron teniendo en cuenta esta restricción e incluyen una forma de especificar la encoding en los datos, por lo que ya no es necesario adivinar.

Lea el mínimo absoluto de cada desarrollador de software Absolutamente, definitivamente debe saber sobre Unicode y conjuntos de caracteres (sin excusas) para obtener más información.

Puede comenzar a obtener la idea aquí java Charset API

Tenga en cuenta que de acuerdo con el doc,

La encoding de caracteres nativos del lenguaje de progtwigción Java es UTF-16

EDITAR:

siento que me llamaron antes de que pudiera terminar esto, tal vez no debería haber publicado la respuesta parcial como estaba. De todos modos, las otras respuestas explican los detalles, el punto es que el juego de caracteres nativo para cada plataforma junto con conjuntos de caracteres alternativos comunes serán leídos correctamente por Java.