metodo manejo diferente declarar comparar caracteres cadenas java string character-encoding

diferente - manejo de cadenas en java



De la compilación al tiempo de ejecución, ¿cómo funciona realmente la codificación de Java String? (4)

Recientemente me di cuenta de que no entiendo completamente el proceso de codificación de cadenas de Java.

Considere el siguiente código:

public class Main { public static void main(String[] args) { System.out.println(java.nio.charset.Charset.defaultCharset().name()); System.out.println("ack char: ^"); /* where ^ = 0x06, the ack char */ } }

Dado que los caracteres de control se interpretan de manera diferente entre windows-1252 e ISO-8859-1 , elegí el carácter ack para la prueba.

Ahora lo compilo con diferentes codificaciones de archivos, UTF-8, windows-1252 e ISO-8859-1 . Ambos compilan exactamente lo mismo, byte por byte según lo verificado por md5sum .

Luego ejecuto el programa:

$ java Main | hexdump -C 00000000 55 54 46 2d 38 0a 61 63 6b 20 63 68 61 72 3a 20 |UTF-8.ack char: | 00000010 06 0a |..| 00000012 $ java -Dfile.encoding=iso-8859-1 Main | hexdump -C 00000000 49 53 4f 2d 38 38 35 39 2d 31 0a 61 63 6b 20 63 |ISO-8859-1.ack c| 00000010 68 61 72 3a 20 06 0a |har: ..| 00000017 $ java -Dfile.encoding=windows-1252 Main | hexdump -C 00000000 77 69 6e 64 6f 77 73 2d 31 32 35 32 0a 61 63 6b |windows-1252.ack| 00000010 20 63 68 61 72 3a 20 06 0a | char: ..| 00000019

Da salida correctamente al 0x06 sin importar qué codificación se esté utilizando.

Ok, todavía genera el mismo 0x06 , que se interpretaría como el carácter imprimible [ACK] por las páginas de código de windows-1252.

Eso me lleva a algunas preguntas:

  1. ¿Se espera que la página de códigos / juego de caracteres del archivo Java compilado sea idéntico al juego de caracteres predeterminado del sistema bajo el cual se está compilando? ¿Son los dos siempre sinónimos?
  2. La representación compilada no parece depender del juego de caracteres en tiempo de compilación, ¿es este el caso?
  3. ¿Esto implica que las cadenas dentro de los archivos Java pueden interpretarse de manera diferente en el tiempo de ejecución si no usan caracteres estándar para el conjunto de caracteres / locale actual?
  4. ¿Qué más debo saber sobre la codificación de cadenas y caracteres en Java?

  1. Los archivos fuente pueden estar en cualquier codificación
  2. Debe decirle al compilador la codificación de los archivos de origen (por ejemplo, javac -encoding... ); de lo contrario, se asume la codificación de la plataforma.
  3. En los binarios de los archivos de clase, los literales de cadena se almacenan como UTF-8 (modificado), pero a menos que trabaje con el código de bytes, esto no importa (consulte la especificación de JVM )
  4. Las cadenas en Java son UTF-16, siempre (consulte las especificaciones del lenguaje Java )
  5. El System.out PrintStream transformará sus cadenas de UTF-16 a bytes en la codificación del sistema antes de escribirlas en la PrintStream

Notas:


Erm basado en this y windows-1252 el carácter de control ACK es exactamente el mismo en ambas codificaciones. La diferencia del enlace que mencionó es que habla de cómo DOS / Windows en realidad tiene símbolos para la mayoría de los caracteres de control en Windows-1252 (como los caracteres Corazón / Club / Espada / Diamante y símiles), mientras que ISO-8859 no lo hace.


Si compila con diferentes codificaciones, estas codificaciones solo afectan sus archivos de origen. Si no tiene ningún carácter especial dentro de sus fuentes, no habrá diferencia en el código de byte resultante.

Para el tiempo de ejecución, se utiliza el conjunto de caracteres predeterminado del sistema operativo. Esto es independiente del juego de caracteres que usaste para compilar.


Un resumen de "qué se debe saber" sobre las codificaciones de cadenas en Java:

  • Una instancia de String , en memoria, es una secuencia de "unidades de código" de 16 bits, que Java maneja como valores char . Conceptualmente, esas unidades de código codifican una secuencia de "puntos de código", donde un punto de código es "el número atribuido a un carácter dado según el estándar de Unicode". Los puntos de código varían desde 0 hasta un poco más de un millón, aunque hasta el momento solo se han definido unos 100 mil. Los puntos de código de 0 a 65535 se codifican en una sola unidad de código, mientras que otros puntos de código utilizan dos unidades de código. Este proceso se llama UTF-16 (también conocido como UCS-2). Hay algunas sutilezas (algunos puntos de código no son válidos, por ejemplo, 65535, y hay un rango de 2048 puntos de código en los primeros 65536 reservados precisamente para la codificación de los otros puntos de código).
  • Las páginas de códigos y similares no afectan la forma en que Java almacena las cadenas en la RAM. Es por eso que "Unicode" comienza con "Uni". Mientras no realice I / O con sus cadenas, estará en el mundo de Unicode, donde todos usan la misma asignación de caracteres para codificar puntos.
  • Los conjuntos de caracteres entran en acción cuando se codifican cadenas en bytes, o se decodifican cadenas a partir de bytes. A menos que se especifique explícitamente, Java utilizará un conjunto de caracteres predeterminado que depende de la "configuración regional" del usuario, una noción agregada difusa de lo que hace que una computadora en Japón hable japonés. Cuando imprime una cadena con System.out.println() , la JVM convertirá la cadena en algo adecuado para donde sea que vayan esos caracteres, lo que a menudo significa convertirlos en bytes utilizando un conjunto de caracteres que depende de la configuración regional actual (o de qué se trata). JVM adivinado de la localización actual).
  • Una aplicación Java es el compilador de Java. El compilador de Java necesita interpretar el contenido de los archivos de origen, que son, a nivel del sistema, solo un montón de bytes. El compilador de Java luego selecciona un juego de caracteres predeterminado para eso, y lo hace dependiendo de la configuración regional actual, al igual que lo haría Java, porque el compilador de Java está escrito en Java. El compilador de Java ( javac ) acepta un indicador de línea de comando ( -encoding ) que se puede usar para anular esa opción predeterminada.
  • El compilador de Java produce archivos de clase que son independientes del entorno local. Los literales de cadena terminan en esos archivos de clase con (tipo de) codificación UTF-8, independientemente del conjunto de caracteres que el compilador de Java usó para interpretar los archivos de origen. La configuración regional del sistema en el que se ejecuta el compilador de Java afecta la forma en que se interpreta el código fuente, pero una vez que el compilador de Java haya entendido que su cadena contiene el punto de código número 6, este punto de código es lo que abrirá camino hacia los archivos de clase. , y ninguna otra. Tenga en cuenta que los puntos de código 0 a 127 tienen la misma codificación en UTF-8, CP-1252 e ISO-8859-1, por lo tanto, lo que obtiene no es una maravilla.
  • Aun así String instancias de String no dependen de ningún tipo de codificación, siempre que permanezcan en la RAM, algunas de las operaciones que puede realizar en las cadenas dependen de la configuración regional. Esto no es una cuestión de codificación; pero una configuración regional también define un "lenguaje" y sucede que las nociones de mayúsculas y minúsculas dependen del idioma que se utiliza. El Sospechoso habitual está llamando a "unicode".toUpperCase() : esto produce "UNICODE" excepto si la configuración regional actual es turca, en cuyo caso obtendrá "UNİCODE" (la " I " tiene un punto). La suposición básica aquí es que si la configuración regional actual es turca, entonces los datos que la aplicación está administrando son probablemente texto turco; Personalmente, considero que esta suposición es, en el mejor de los casos, cuestionable. Pero así es.

En términos prácticos, debe especificar codificaciones explícitamente en su código, al menos la mayoría del tiempo. No llame a String.getBytes() , llame a String.getBytes("UTF-8") . El uso de la codificación predeterminada, dependiente de la configuración regional, está bien cuando se aplica a algunos datos intercambiados con el usuario, como un archivo de configuración o un mensaje para mostrar de inmediato; pero en otros lugares, evite los métodos dependientes del entorno local siempre que sea posible.

Entre otras partes de Java dependientes de la ubicación, hay calendarios. Existe todo el negocio de la zona horaria, que depende de la "zona horaria", que debe relacionarse con la posición geográfica de la computadora (y esto no es parte de la "localidad" en sentido estricto ...). Además, innumerables aplicaciones de Java fallan misteriosamente cuando se ejecutan en Bangkok, porque en una ubicación tailandesa, Java utiliza por defecto el calendario budista según el cual el año actual es 2553.

Como regla general, suponga que el mundo es vasto (¡lo es!) Y mantenga las cosas genéricas (no haga nada que dependa de un juego de caracteres hasta el último momento, cuando la E / S debe realizarse).