Problemas de codificación de caracteres con Javac y JVM

  Javac y Java son herramientas que vienen con JDK. Javac es una herramienta de compilación. Las herramientas Java inician la máquina virtual JVM y ejecutan programas java. Ambas herramientas tienen opciones para configurar la codificación de caracteres. Este artículo analiza los escenarios de uso de las opciones de codificación de caracteres y las causas de los caracteres ilegibles. Escriba la conclusión aquí primero. Si no desea leer los siguientes capítulos, solo mire la conclusión aquí.

  Nota: La codificación de caracteres y el conjunto de caracteres en el texto son el mismo concepto. Tengo un blog dedicado anteriormente a este tema: https://www.cnblogs.com/jayson-jamaica/p/12652873.html

Conclusión: las opciones de codificación de caracteres de Javac

  • Forma: codificación javac CharSet XXXX.java // CharSet es la codificación de caracteres del archivo XXXX.java.
  • El compilador javac analiza el archivo .java de acuerdo con la codificación de caracteres seguida de -encoding. Cuando no se establece la codificación, el conjunto de caracteres predeterminado del sistema se utiliza para analizar el archivo .java. El conjunto de caracteres predeterminado para Windows es GBK.
  • No importa qué codificación use el archivo .java anterior, el archivo .class compilado usa la codificación utf8.
  • Si la codificación de caracteres especificada por -encoding es inconsistente con la codificación de caracteres del archivo .java, no necesariamente puede fallar al compilar, lo que enterrará problemas ocultos para los siguientes caracteres ilegibles.

Conclusión: opciones de codificación de caracteres JVM

  • Formulario: java -Dfile.encoding = CharSet XXXX // XXXX es un archivo de clase, CharSet es un conjunto de caracteres compatible con el dispositivo local.
  • -Dfile.encoding no establece la codificación de caracteres de la memoria de la máquina virtual JVM. La codificación de caracteres de la memoria de la máquina virtual JVM no se puede establecer, todos son UTF-16. 
  • La JVM carga los caracteres en el archivo .class y lo convierte a UTF-16 y lo almacena en la memoria. Codifique o decodifique de acuerdo con el conjunto de caracteres después de la opción de codificación cuando los caracteres necesiten interactuar con el dispositivo local.
  • -Dfile.encoding no está configurado, use el juego de caracteres predeterminado del sistema, el juego de caracteres predeterminado para Windows es GBK.

  Las opciones de codificación de caracteres de la JVM no son fáciles de entender. Por ejemplo, el siguiente código se utiliza para ilustrar:

String str = "脑袋 里 有 一盆 酱"; 
OutputStream outputStream = nuevo FileOutputStream ("D: \\ test \\ t.txt"); 
outputStream.write (str.getBytes ());

  El código anterior es escribir varios caracteres chinos en el archivo t.txt. Después de ejecutar el programa, la codificación de caracteres del archivo t.txt es GBK o UTF-8, que depende de la opción -Dfile.encoding, si la opción se especifica como GBK , Entonces la codificación de caracteres del archivo t.txt es GBK; si se especifica como UTF-8, entonces la codificación de caracteres del archivo t.txt es UTF-8.

Explicación detallada: opciones de codificación de caracteres Javac y JVM

  Este blog está muy bien escrito, https://blog.csdn.net/lgh1992314/article/details/77482046 . Leí este blog para entender muchos detalles. Muchos lugares en la parte posterior también citarán las fotos y opiniones de este blog, gracias primero. Vamos a la imagen primero.

  La imagen de arriba muestra los diversos procesos del archivo .java desde la compilación hasta la ejecución. Estos procesos implican la codificación de caracteres. La explicación específica se extrae de la siguiente manera:

① A.java es un archivo de texto (almacenado en un cierto formato de codificación: UTF-8, GBK, ISO-8859-1, etc.), el compilador de Java debe analizar este archivo de texto y compilarlo para generar un archivo .class. Para analizarlo, debe conocer su método de codificación. (Conjunto de caracteres de codificación Javac) Si la codificación especificada por la codificación es inconsistente con la codificación del archivo, la compilación falla o los caracteres en el archivo de la clase son ilegibles.

②: A.java codificado en diferentes métodos de codificación es compilado por el compilador de Java para generar la misma clase A. (La cadena de caracteres se almacena en formato UTF-8) Para la interpretación del código de bytes, consulte: http://blog.csdn.net/x_iya/article/details/77073112

③: La máquina virtual Java carga A.class en forma de una secuencia de bytes binarios. La codificación de caracteres en A.class es utf8. Después de cargar en la memoria de la máquina virtual JVM, la codificación de caracteres es utf16.

④: Resultado de salida: si se especifica un juego de caracteres en el código, se enviará al dispositivo de acuerdo con el juego de caracteres especificado en el código. Si el juego de caracteres no se especifica en el código, se envía al dispositivo de acuerdo con el juego de caracteres especificado por -Dfile.encoding al inicio de JVM. Si el dispositivo no admite el conjunto de caracteres, se muestran caracteres confusos.

Opciones de codificación de caracteres Javac

  El proceso 1 en la figura anterior es el proceso de compilación de Java. La herramienta de compilación javac que viene con el JDK admite la configuración de la opción de codificación de la siguiente forma:

javac -coding CharSet xxxx.java // CharSet debe ser el conjunto de caracteres del archivo xxx.java

   javac analiza los caracteres en xxxx.java de acuerdo con el conjunto de caracteres especificado por la codificación.Si el conjunto de caracteres especificado por la codificación es coherente con el conjunto de caracteres del archivo .java, se puede analizar y compilar normalmente. Si el conjunto de caracteres especificado por la codificación es inconsistente con el conjunto de caracteres del archivo .java, hay dos resultados del análisis: uno es el error de análisis y el error de compilación; el otro es el análisis de otros caracteres y la compilación es exitosa, pero la codificación de caracteres en el archivo de clase Hay errores (es decir, caracteres confusos).

experimento de compilación javac
String str = "脑袋 里 有 一盆 酱" ; 
OutputStream outputStream = nuevo FileOutputStream ("D: \\ test \\ t.txt" ); 
outputStream.write (str.getBytes ());

  De acuerdo con el código anterior, cree dos clases Java, a saber, GbkCode y Utf8Code, GbkCode usa el juego de caracteres gbk, Utf8Code usa el juego de caracteres utf-8, el código anterior está organizado en dos clases. Utilice javac, javac -encoding gbk, javac -encoding utf8 para compilar dos archivos java respectivamente. Luego verifique la codificación de caracteres del archivo de clase. Obtenga la siguiente tabla.

Compilar comando Resultado de compilación Codificación de caracteres del archivo de clase
javac GbkCode.java Exitoso, generar el archivo GBKCode.class Codificación UTF-8
codificación javac gbk GbkCode.java Exitoso, generar el archivo GBKCode.class Codificación UTF-8
javac -coding utf8 GbkCode.java Falla, utf8 carácter no asignable xxxx \
javac Utf8Code.java Fallo, gbk carácter no asignable xxxx \
javac -coding gbk Utf8Code.java Fallo, gbk carácter no asignable xxxx \
javac -coding utf8 Utf8Code.java Exitoso, genera el archivo Utf8Code.class Codificación UTF-8

  Nota: Hay muchas maneras de juzgar la codificación de caracteres de los archivos de clase. Utilicé Notepad ++ para confirmar la codificación de los archivos de clase. Use notepad ++ para abrir el archivo de clase, ver el binario y buscar la secuencia binaria correspondiente a utf8 de "Hay una olla de salsa en la cabeza". Si se encuentra, significa codificación utf-8. Para conocer el método de visualización de binarios en Notepad ++, consulte mi otro blog: https://www.cnblogs.com/jayson-jamaica/p/12659229.html

  El experimento 1 probó varias coincidencias entre el conjunto de caracteres especificado por la opción de codificación y el conjunto de caracteres del archivo .java. La conclusión es que el conjunto de caracteres especificado por la opción de codificación es consistente con el conjunto de caracteres del archivo .java, y la compilación es exitosa; si no es consistente, la compilación falla. Pero este experimento no es suficiente, porque si la compilación falla si es inconsistente, entonces nunca habrá un problema confuso en el proceso de compilación. Entonces continúa el experimento 2.

experimento de compilación javac 2

  Traté de hacer una cadena para ver si podía engañar al compilador. Encontré un par de cadenas a través de Notepad ++. La codificación utf8 de "People" es consistente con la codificación gbk de "浜 餜", así que modifiqué el código anterior de la siguiente manera:

String str = "People"; // Use la codificación utf8, guarde en el archivo Utf8Code.java, use la codificación gbk para compilar. 
String str = "浜養 皯 "; // Codificar con gbk, guardar en el archivo GbkCode.java, compilar con la codificación utf8.

  Efectivamente, engañé al compilador, y la codificación en el archivo de clase parecía confusa. Los resultados son los siguientes:

Compilar comando Resultado de compilación Codificación de caracteres del archivo de clase
javac -coding gbk Utf8Code.java Exitoso, genera el archivo Utf8Code.class Debería ser la codificación utf8 de "personas", que se convirtió en la codificación utf8 de "浜 餜"
javac -coding utf8 GbkCode.java Exitoso, generar el archivo GBKCode.class El código utf8 que se suponía que era "Beng Xuanqiang" se convirtió en el código utf8 para "personas".

  El experimento 2 demostró que en algunos casos, el conjunto de caracteres especificado por la opción de codificación es inconsistente con el conjunto de caracteres del archivo .java, pero también se puede compilar correctamente, pero hay un problema con la codificación en el archivo de clase. Esto provocará caracteres confusos durante la ejecución.

Opciones de codificación de caracteres JVM

  Utilice el comando java XXXX para iniciar la máquina virtual JVM y ejecutar el archivo XXXX.class. Como se mencionó anteriormente, la codificación de caracteres en el archivo de clase es utf8. El carácter almacenado en la máquina virtual JVM utiliza la codificación de caracteres utf16 y no se puede establecer. Ese java -Dfile.encoding=CharSet xxxx`¿Qué papel es?

  La máquina virtual JVM a veces realiza operaciones de E / S con un dispositivo local, por lo que es necesario conocer la codificación de caracteres compatible con el dispositivo. -Dfile.encoding es decirle a la máquina virtual la codificación de caracteres del dispositivo local. Algunos dispositivos admiten codificaciones de caracteres múltiples, como archivos; algunos dispositivos solo admiten codificación de un carácter, como terminal / Terminal / Console, etc. Los siguientes dos experimentos se verifican por separado.

Experimento de codificación Java 1
String str = "脑袋 里 有 一盆 酱" ; 
OutputStream outputStream = nuevo FileOutputStream ("D: \\ test \\ t.txt" ); 
outputStream.write (str.getBytes ());

  Sigue siendo este código, que se codifica utilizando utf8 / gbk y se compila con la opción de codificación correcta; utilizando diferentes opciones de codificación de archivo para que los archivos de clase se ejecuten, se obtienen los siguientes resultados:

Compilar comando Comando de ejecución Codificación de caracteres del archivo t.txt
javac -coding utf8 Utf8Code.java
java -Dfile.encoding = UTF-8 Utf8Code UTF-8
java -Dfile.encoding = GBK Utf8Code GBK
javac -encoding gbk GBKCode.java
java -Dfile.encoding = UTF-8 GBKCode UTF-8
java -Dfile.encoding = GBK GBKCode GBK

  Nota: Antes de usar el comando java para ejecutar el programa, debe eliminar el archivo t.txt obtenido la última vez. De lo contrario, los resultados obtenidos pueden ser anormales.

  A través de este experimento, puede obtener el propósito de -Dfile.encoding: adaptar el conjunto de caracteres compatible con el dispositivo local (este experimento es adaptar el conjunto de caracteres del sistema de archivos, se estima que la adaptación del conjunto de caracteres de red IO también es la misma). Cuando escribe el código de prueba, parece que prefiere usar la forma de impresión de consola en lugar de imprimir los resultados en un archivo. El siguiente experimento probará la impresión de caracteres en la consola.

Experimento de codificación Java 2
String str = "Hay una olla de salsa en mi cabeza" ; 
System.out.println (str); 
System.out.println (Arrays.toString (str.getBytes ())); // El propósito de esta línea de código es verificar que str es realmente Afectado por -Dfile.encoding.

   Este código imprime el resultado en la consola. Use la codificación .utf8 / gbk para escribir archivos .java, compile con la opción de codificación correcta; use diferentes opciones de codificación de archivo para archivos de clase. Para que los resultados de la prueba sean más convincentes, se utilizan tres tipos de terminales para la ejecución. Una es la línea de comando cmd que viene con Windows, otra es la Terminal Cygwin64 y la otra es la Terminal de Ubuntu.

  Permítanme explicar primero el conjunto de caracteres predeterminado del terminal: mi computadora es un sistema win10, y la línea de comando cmd y el terminal Cygwin64 usan la codificación de caracteres GBK por defecto; Ubuntu Terminal usa la codificación de caracteres utf8 de forma predeterminada.

  Para el conjunto de caracteres predeterminado del terminal, simplemente puede probar y verificar y usar los comandos para ver el archivo de texto utf8 y el archivo de texto gbk respectivamente. Si el archivo de texto gbk se muestra normalmente y el archivo de texto utf8 está confuso, el conjunto de caracteres predeterminado del terminal es gbk. El comando para ver el archivo en cmd es el mismo que el comando en el type 文件名terminal cygwin64, y el comando para ver el archivo es cat 文件名. El enfoque específico puede referirse al siguiente blog: https://blog.csdn.net/lgh1992314/article/details/77482046

Los resultados del experimento son los siguientes:

Compilar comando Comando de ejecución salida de consola cmd Salida terminal Cygwin64 Salida de Ubuntu Ternimal
javac -coding utf8 Utf8Code.java
java -Dfile.encoding = GBK Utf8Code Normal Normal Confuso
java -Dfile.encoding = UTF-8 Utf8Code Normal Confuso Normal
javac -encoding gbk GBKCode.java
java -Dfile.encoding = GBK GBKCode Normal Normal Confuso
java -Dfile.encoding = UTF-8 Utf8Code Normal Confuso Normal

  Los resultados de este experimento son más interesantes: los resultados de la consola win10 cmd son normales y los resultados no parecen ser capaces de inferir nada, lo cual se discutirá más adelante. Se esperan los resultados de Cygwin64 Terminal y Ubuntu Terminal. El formato de codificación predeterminado de Cygwin64 Terminal es GBK, por lo que Dfile.encoding = UTF-8 confundirá el archivo de ejecución, y Dfile.encoding = GBK se muestra normalmente. El formato de codificación predeterminado de Ubuntu Terminal es utf8, por lo que Dfile.encoding = UTF-8 se muestra normalmente y Dfile.encoding = GBK muestra caracteres ilegibles.

  UbuntuTerminal es muy conveniente para configurar el conjunto de caracteres. Haremos más pruebas para probarlo. Los resultados de la prueba son los siguientes:

Comando de ejecución Conjunto de caracteres de terminal utf8 Conjunto de caracteres de terminal gbk Conjunto de caracteres del terminal gb18030
-Dfile.encoding = UTF-8 Normal Confuso Confuso
-Dfile.encoding = GBK Confuso Normal Normal
-Dfile.encoding = gb18030 Confuso Normal Normal

  Dado que el juego de caracteres gbk es compatible con gb18030, cuando el juego de caracteres Terminal es gb18030, -Dfile.encoding = el archivo de ejecución GBK no tiene caracteres ilegibles. Por lo tanto, en el Experimento 2, se puede concluir que -Dfile.encoding establece que la codificación de caracteres sea compatible con la codificación de caracteres del terminal / consola / Terminal.

Resumen:

  Sobre el problema de codificación de Javac y el problema de java -Dfile.encoding, ya está claro.

  • El conjunto de caracteres seguido de la opción de codificación de javac debe ser el mismo que el conjunto de caracteres utilizado por el archivo .java; de lo contrario, se generarán caracteres ilegibles en el archivo de clase.
  • El juego de caracteres seguido de java -Dfile.encoding debe ser compatible con el juego de caracteres admitido por el dispositivo. El dispositivo aquí puede ser un sistema de archivos, un terminal o una red. Todavía no he aprendido la parte de la red, así que no lo discutiré por ahora.

 

Escribe al final:

  Para comprender los problemas de codificación de caracteres de javac y JVM, pasé más de una semana, leí muchos blogs, hice muchos experimentos e incluso instalé una máquina virtual y Ubuntu específicamente. Pero todavía quedan dos problemas. Uno es el terminal cmd que viene con Win 10. Cuando se ejecuta el archivo de clase, no aparecerán caracteres ilegibles independientemente del conjunto de caracteres seguido de -Dfile.encoding; El segundo problema es que cuando el conjunto de caracteres del terminal Cygwin64 se cambia a utf8, -Dfile .encoding = UTF-8, los caracteres confusos todavía se imprimen.

  Para estos dos problemas restantes, supongo que hay una optimización mutua entre el terminal del sistema Win10 y la máquina virtual JVM. Cuando la JVM ejecuta el archivo de clase, leerá el conjunto de caracteres predeterminado de Win10. Al imprimir la salida, independientemente de la configuración de -Dfile.encoding El conjunto de caracteres se convertirá en salida GBK. Después de que Cygwin64 Terminal cambia el conjunto de caracteres a utf8, cuando la JVM ejecuta el archivo de clase, sigue leyendo que el conjunto de caracteres predeterminado de Win10 es GBK, por lo que, independientemente del conjunto de caracteres establecido por -Dfile.encoding, la salida es GBK, mientras que Cygwin64 Terminal solo puede mostrar UTF8, por lo tanto, genera caracteres ilegibles.

  Para estos dos problemas restantes, no es fácil de verificar. Necesita una computadora win con el conjunto de caracteres predeterminado de utf8, o cambie el conjunto de caracteres del sistema de win10 a utf8. Permítame dejarlo aquí por el momento.

Los siguientes blogs de referencia están bien escritos y se pueden usar como referencia.

https://www.qqxiuzi.cn/bianma/zifuji.php

https://www.qqxiuzi.cn/bianma/Unicode-UTF.php

https://blog.csdn.net/PacosonSWJTU/article/details/79118928

https://www.ibm.com/developerworks/cn/java/j-lo-chinesecoding/

https://blog.csdn.net/lgh1992314/article/details/77482046

https://www.iflym.com/index.php/code/201405250001.html

Supongo que te gusta

Origin www.cnblogs.com/jayson-jamaica/p/12695427.html
Recomendado
Clasificación