Cada región del mundo tiene su propio idioma local. Las diferencias regionales conducen directamente a diferencias en el entorno lingüístico. En el proceso de desarrollo de un programa internacional, es importante abordar las cuestiones lingüísticas.
Este es un problema que existe en todo el mundo, por lo que Java proporciona una solución mundial. El método descrito en este artículo es para procesar chino, pero por extensión, también es aplicable para procesar idiomas de otros países y regiones del mundo.
Los caracteres chinos son de doble byte. El llamado doble byte significa que una palabra doble ocupa dos posiciones de BYTE (es decir, 16 bits), que se denominan bits altos y bits bajos respectivamente. La codificación de caracteres chinos especificada en China es GB2312, que es obligatoria. Actualmente, casi todas las aplicaciones que pueden procesar chino admiten GB2312. GB2312 incluye caracteres chinos de primer y segundo nivel y 9 símbolos de área. Los bits altos van de 0xa1 a 0xfe, y los bits bajos también van de 0xa1 a 0xfe. Entre ellos, el rango de codificación de los caracteres chinos es de 0xb0a1 a 0xf7fe.
Hay otra codificación llamada GBK, pero es una especificación, no obligatoria. GBK proporciona 20902 caracteres chinos, que son compatibles con GB2312, y el rango de codificación es de 0x8140 a 0xfefe. Todos los caracteres de GBK se pueden asignar a Unicode 2.0 uno por uno.
En un futuro próximo, China promulgará otro estándar: GB18030-2000 (GBK2K). Incluye fuentes de tibetanos, mongoles y otras minorías étnicas, lo que resuelve fundamentalmente el problema de las posiciones insuficientes de los caracteres. Nota: Ya no tiene una longitud fija. La parte de dos bytes es compatible con GBK y la parte de cuatro bytes son caracteres extendidos y glifos. Su primer y tercer bytes van de 0x81 a 0xfe, y su segundo y cuarto bytes van de 0x30 a 0x39.
Este artículo no pretende presentar Unicode. Aquellos que estén interesados pueden navegar por "http://www.unicode.org/" para ver más información. Unicode tiene una característica: incluye todos los glifos de caracteres del mundo. Por lo tanto, los idiomas de varias regiones pueden establecer relaciones de mapeo con Unicode, y Java aprovecha esto para lograr la conversión entre idiomas heterogéneos.
En JDK, las codificaciones relacionadas con el chino son:
Tabla 1 Lista de codificaciones relacionadas con el chino en JDK
Descripción | del nombre de codificación |
ASCII | de 7 bits, igual que ascii7 |
ISO8859-1 | de 8 bits, igual que 8859_1, ISO-8859-1, ISO_8859-1, latin1... etc. |
GB2312-80 | 16 bits, igual que gb2312, gb2312 -1980, EUC_CN, euccn, 1381, Cp1381, 1383, Cp1383, ISO2022CN, ISO2022CN_GB... etc. son iguales |
que | GBK |
UTF8 | distingue entre |
mayúsculas | y minúsculas |
Lo mismo que cp1392 y 1392. Actualmente, pocos JDK son compatibles |
en la práctica al programar, con los que entro en contacto más son GB2312 (GBK) e ISO8859-1.
¿Por qué hay un signo "?"
Como se mencionó anteriormente, la conversión entre diferentes idiomas se completa mediante Unicode. Supongamos que hay dos idiomas diferentes, A y B. Los pasos de conversión son: primero convertir A a Unicode y luego convertir Unicode a B.
Dar ejemplos. Hay un carácter chino "李" en GB2312 y su código es "C0EE", que debe convertirse al código ISO8859-1. Los pasos son: primero convierta el carácter "李" a Unicode para obtener "674E" y luego convierta "674E" a caracteres ISO8859-1. Por supuesto, esta asignación no tendrá éxito porque no hay ningún carácter correspondiente a "674E" en ISO8859-1.
¡El problema ocurre cuando el mapeo no tiene éxito! Al convertir de un determinado idioma a Unicode, si el carácter no existe en un determinado idioma, el resultado será el código Unicode "uffffd" ("u" significa codificación Unicode). Al convertir de Unicode a un determinado idioma, si un determinado idioma no tiene los caracteres correspondientes, obtendrá "0x3f" ("?"). De aquí viene el "?".
Por ejemplo: realice la nueva operación String(buf, "gb2312") en el flujo de caracteres buf = "0x80 0x40 0xb0 0xa1", el resultado será "ufffdu554a", y luego imprímalo, el resultado será " ?ah", porque "0x80 0x40" es un carácter en GBK, no en GB2312.
Para otro ejemplo, realice la nueva operación String (buf.getBytes("GBK")) en la cadena String="u00d6u00ecu00e9u0046u00bu00f9", y el resultado es "3fa8aca8a6463fa8b4", entre los cuales, "u00d6 "No hay ningún carácter correspondiente en "GBK", por lo que obtenemos "3f", "u00ec" corresponde a "a8ac", "u00e9" corresponde a "a8a6" y "0046" corresponde a "46" (porque es un carácter ASCII), no se encontró "u00b" y se obtuvo "3f". Finalmente, "u00f9" corresponde a "a8b4". Imprima esta cadena y el resultado es "?ìéF?ù". ¿Viste eso? No todo son signos de interrogación aquí, porque el contenido asignado entre GBK y Unicode incluye caracteres además de los caracteres chinos. Este ejemplo es la mejor prueba.
Por lo tanto, al transcodificar caracteres chinos, si se produce confusión, es posible que no necesariamente aparezcan signos de interrogación. Sin embargo, después de todo, un error es un error. No existe una diferencia cualitativa entre 50 pasos y 100 pasos.
O puede preguntar: ¿Cuál será el resultado si se incluye en el juego de caracteres de origen pero no en Unicode? La respuesta es no lo sé. Porque no tengo a mano el conjunto de caracteres fuente para hacer esta prueba. Pero una cosa es segura: el conjunto de caracteres de origen no está lo suficientemente estandarizado. En Java, si esto sucede, se generará una excepción.
¿Qué es UTF?
UTF es la abreviatura de Unicode Text Format, que significa formato de texto Unicode. Para UTF, se define de la siguiente manera:
(1) Si los primeros 9 bits de un carácter Unicode de 16 bits son 0, se representa mediante un byte. El primer bit de este byte es "0" y los 7 bits restantes. son iguales que el carácter original. Los últimos 7 dígitos son iguales, como "u0034" (0000 0000 0011 0100), representado por "34" (0011 0100)
; 2) Si los primeros 5 caracteres de 16 bits de Unicode son 0, se representan con 2 bytes. El primer byte comienza con "110" y los siguientes 5 bits son los mismos que los 5 bits más altos de la fuente. carácter después de excluir los primeros 5 ceros; el segundo byte comienza con "10" Al principio, los siguientes 6 bits son los mismos que los 6 bits inferiores del carácter de origen. Por ejemplo, "u025d" (0000 0010 0101 1101) se convertirá en "c99d" (1100 1001 1001 1101);
(3) Si no cumple con las dos reglas anteriores, se representará con tres bytes. El primer byte comienza con "1110" y los últimos cuatro bits son los cuatro bits superiores del carácter de origen; el segundo byte comienza con "10" y los últimos seis bits son los seis bits del medio del tercer carácter; el byte comienza con "10". A partir de "10", los últimos seis dígitos son los seis dígitos inferiores del carácter de origen, por ejemplo, "u9da7" (1001 1101 1010 0111) se convierte en "e9b6a7" (1110 1001 1011). 0110 1010 0111);
la diferencia entre Unicode y Unicode en programas JAVA se puede describir así. La relación entre UTF no es absoluta: cuando una cadena se ejecuta en la memoria, aparece como un código Unicode y cuando se guarda en un archivo. u otros medios, se utiliza UTF. Este proceso de conversión se completa con writeUTF y readUTF.
Bien, la discusión básica casi ha terminado, vayamos al grano.
Primero piense en el problema como una caja negra. Primero veamos la representación de primer nivel del cuadro negro:
entrada (charsetA) -> proceso (Unicode) -> salida (charsetB)
es simple. Este es un modelo IPO, es decir, entrada, procesamiento y salida. El mismo contenido debe convertirse de charsetA a Unicode y luego a charsetB.
Veamos la representación secundaria:
SourceFile(jsp,java)->class->output.
En esta figura, se puede ver que la entrada son archivos fuente jsp y java. Durante el procesamiento, el archivo Class se utiliza como soporte. y luego salida. Luego, refinelo al tercer nivel:
jsp->archivo temporal->clase->navegador,consola os,
aplicación db,servlet->clase->navegador,consola os,db
. Esta imagen será más clara. El archivo Jsp primero genera el archivo Java intermedio y luego genera la Clase. Los servlets y las aplicaciones ordinarias se compilan directamente para generar Clase. Luego, envíe la salida de la clase al navegador, consola o base de datos, etc.
JSP: el proceso desde el archivo fuente hasta la clase
El archivo fuente de Jsp es un archivo de texto que termina en ".jsp". En esta sección, se explicará el proceso de interpretación y compilación de archivos JSP y se realizará un seguimiento de los cambios en chino.
1. La herramienta de conversión JSP (jspc) proporcionada por el motor JSP/Servlet busca el juego de caracteres especificado en <%@ page contentType ="text/html; charset=<Jsp-charset>"%> en el archivo JSP. Si no se especifica <Jsp-charset> en el archivo JSP, se utiliza la configuración predeterminada file.encoding en la JVM. En circunstancias normales, este valor es ISO8859-1.
jspc usa el equivalente a "javac –encoding <Jsp; El comando -charset> " interpreta todos los caracteres que aparecen en el archivo JSP, incluidos los caracteres chinos y ASCII, y luego convierte estos caracteres en caracteres Unicode, luego los convierte al formato UTF y los guarda como archivos JAVA. Al convertir caracteres ASCII en caracteres Unicode, simplemente agrega "00" al frente, como "A", que se convierte en "u0041" (no se necesita ningún motivo, así es como se compila la tabla de códigos Unicode). Luego, después de la conversión a UTF, ¡volvió a cambiar a "41"! Es por eso que puede usar un editor de texto común para ver los archivos JAVA generados por JSP;
3. El motor usa el comando equivalente a "javac -encoding UNICODE"
para compilar los archivos JAVA en archivos CLASS;
situación de conversión de estos procesos. Existe el siguiente código fuente:
<%@ page contentType="text/html; charset=gb2312"%>
<html><cuerpo>
<%
Cadena a="chino";
salida.println(a);
%>
</body></html>
Este código fue escrito en UltraEdit para Windows. Después de guardar, la codificación hexadecimal de los dos caracteres "chinos" es "D6 D0 CE C4" (codificación GB2312). Después de buscar en la tabla, la codificación Unicode de la palabra "chino" es "u4E2Du6587", que en UTF es "E4 B8 AD E6 96 87". Abra el archivo JAVA convertido a partir del archivo JSP generado por el motor y descubra que la palabra "chino" ha sido reemplazada por "E4 B8 AD E6 96 87". Luego verifique el archivo CLASS generado por la compilación del archivo JAVA y busque. que el resultado sea exactamente el mismo que en el archivo JAVA.
Veamos la situación en la que el CharSet especificado en JSP es ISO-8859-1.
<%@ página contentType="text/html; charset=ISO-8859-1"%>
<html><cuerpo>
<%
Cadena a="chino";
salida.println(a);
%>
</body></html>
De manera similar, este archivo está escrito con UltraEdit y los dos caracteres "chinos" también se almacenan como codificación GB2312 "D6 D0 CE C4". Primero simule el proceso de generación de archivos JAVA y archivos CLASS: jspc usa ISO-8859-1 para interpretar "chino" y lo asigna a Unicode. Dado que ISO-8859-1 es de 8 bits y es un idioma latino, su regla de mapeo es agregar "00" antes de cada byte, por lo que la codificación Unicode mapeada debe ser "u00D6u00D0u00CEu00C4", después de la conversión a UTF debería ser "C3 96 C3 90 C3 8E C3 84". Bien, abra el archivo y eche un vistazo. En el archivo JAVA y en el archivo CLASS, "chino" se expresa como "C3 96 C3 90 C3 8E C3 84".
Si no se especifica <Jsp-charset> en el código anterior, es decir, la primera línea se escribe como "<%@ page contentType="text/html" %>", JSPC utilizará la configuración file.encoding para interpretar el Archivo JSP. En RedHat 6.2, el resultado del procesamiento es exactamente el mismo que el especificado ISO-8859-1.
Hasta ahora, se ha explicado el proceso de mapeo de caracteres chinos en el proceso de conversión de archivos JSP a archivos CLASS. En una palabra: de "JspCharSet a Unicode a UTF". La siguiente tabla resume este proceso:
Tabla 2 Proceso de conversión "chino" de JSP a CLASS
Jsp-CharSet | En archivo JSP En | archivoJAVA | En archivo CLASS |
GB2312 | D6 D0 CE C4 (GB2312) | de u4E2Du6587 (Unicode) a E4 B8 AD E6 96 87 (UTF) | E4 B8 AD E6 96 87 (UTF) |
ISO-8859 -1 | D6 D0 CE C4 (GB2312) | de u00D6u00D0u00CEu00C4 (Unicode) a C3 96 C3 90 C3 8E C3 84 (UTF) | C3 96 C3 90 C3 8E C3 84 (UTF) |
Ninguno (predeterminado = codificación de archivo) | Igual que ISO- 8859 -1 | Igual que ISO-8859-1 | Igual que ISO-8859-1 |
Servlet: el proceso desde el archivo fuente hasta la clase.
El archivo fuente del servlet es un archivo de texto que termina en ".java". Esta sección discutirá el proceso de compilación de Servlet y rastreará los cambios chinos.
Utilice "javac" para compilar el archivo fuente del servlet. javac puede tomar el parámetro "-encoding <Compile-charset>", que significa "usar la codificación especificada en <Compile-charset> para interpretar el archivo fuente de Serlvet".
Cuando se compila el archivo fuente, utilice <Compile-charset> para interpretar todos los caracteres, incluidos los caracteres chinos y ASCII. Luego convierta las constantes de caracteres en caracteres Unicode y, finalmente, convierta Unicode a UTF.
En Servlet, hay otro lugar para configurar el CharSet del flujo de salida. Por lo general, antes de generar el resultado, se llama al método setContentType de HttpServletResponse para lograr el mismo efecto que configurar <Jsp-charset> en JSP, que se llama <Servlet-charset>.
Tenga en cuenta que en el artículo se mencionan un total de tres variables: <Jsp-charset>, <Compile-charset> y <Servlet-charset>. Entre ellos, los archivos JSP solo están relacionados con <Jsp-charset>, mientras que <Compile-charset> y <Servlet-charset> solo están relacionados con Servlet.
Mire el siguiente ejemplo:
import javax.servlet.*;
import javax.servlet.http.*
clase testServlet extiende HttpServlet
{
doGet público vacío (solicitud HttpServletRequest, respuesta HttpServletResponse)
lanza ServletException, java.io.IOException
{
resp.setContentType("texto/html; charset=GB2312");
java.io.PrintWriter out=resp.getWriter();
out.println("<html>");
out.println("#中文#");
out.println("</html>");
}
}
Este archivo también está escrito con UltraEdit para Windows y los dos caracteres "chino" se guardan como "D6 D0 CE C4" (codificación GB2312).
Comience a compilar. La siguiente tabla muestra el código hexadecimal de la palabra "chino" en el archivo CLASS cuando <Compile-charset> es diferente. Durante la compilación, <Servlet-charset> no tiene ningún efecto. <Servlet-charset> solo afecta la salida del archivo CLASS. De hecho, <Servlet-charset> y <Compile-charset> trabajan juntos para lograr el mismo efecto que <Jsp-charset> en el archivo JSP, porque <Jsp-. charset >Tendrá un impacto en la compilación y salida de archivos CLASS.
Tabla 3 El proceso de transformación de "chino" del archivo fuente de servlet a clase
Compile-charset | El código Unicode equivalente | en el archivo Class | en el archivo fuente del Servlet | es
GB2312 | D6 D0 CE C4 (GB2312) | E4 B8 AD E6 96 87 (UTF) | u4E2Du6587 (en Unicode = "chino") |
ISO-8859-1 | D6 D0 CE C4 (GB2312) | C3 96 C3 90 C3 8E C3 84 (UTF) | u00D6 u00D0 u00CE u00C4 (Se agrega A 00 delante de D6 D0 CE C4) |
Ninguno (predeterminado) | D6 D0 CE C4 (GB2312) | Igual que ISO- 8859 -1 | Igual que ISO-8859-1 |
No. | Paso Descripción | Resultado |
1 | Escriba el archivo fuente JSP y guárdelo en formato GB2312 | D6 D0 CE C4 (D6D0=中文 CEC4=文) |
2 | jspc convierte el archivo fuente JSP en un archivo JAVA temporal, asigna la cadena a Unicode de acuerdo con GB2312 y la escribe en el archivo JAVA en formato UTF | E4 B8 AD E6 96 87 |
3 | Compile el archivo temporal Archivo JAVA en un archivo CLASS | E4 B8 AD E6 96 87 |
4 | Al ejecutar, primero lea la cadena del archivo CLASS usando readUTF. La codificación Unicode en la memoria es | 4E 2D 65 87 (en Unicode 4E2D=中文6587=文) |
5Según | . Jsp -charset=GB2312 Convertir Unicode a flujo de bytes | D6 D0 CE C4 |
6 | Envíe el flujo de bytes a IE y establezca la codificación de IE en GB2312 (Nota del autor: esta información está oculta en el encabezado HTTP) | D6 D0 CE C4 |
7 | Vista de IE resultados "chino" con | "chino simplificado" (visualización correcta) |
No. | Paso Descripción | Resultado | |
1 | Escriba el archivo fuente JSP y guárdelo en formato GB2312 | D6 D0 CE C4 (D6D0=中文 CEC4=文) | |
2 | jspc convierte el archivo fuente JSP en un archivo JAVA temporal, asigna la cadena a Unicode de acuerdo con ISO8859-1 y la escribe en el archivo JAVA en formato UTF | C3 96 C3 90 C3 8E C3 | |
84 | 3 | El archivo JAVA temporal se compila en un archivo CLASS | C3 96 C3 90 C3 8E C3 84 |
4. | Cuando se ejecuta, primero lea la cadena del archivo CLASS usando readUTF. La codificación Unicode en la memoria es | 00 D6 00 D0 00 CE 00 C4. (¡¡¡Nada!!!) | |
5 | Convierta Unicode en flujo de bytes | D6 D0 CE C4 | de acuerdo con Jsp-charset=ISO8859-1|
6 | Envíe el flujo de bytes a IE y establezca la codificación de IE en ISO8859-1 (prensa del autor: esta información es oculto en el encabezado HTTP) | D6 D0 CE C4 | |
7 | IE usa "caracteres de Europa occidental" para ver el resultado | como caracteres confusos. En realidad, son cuatro caracteres ASCII, pero debido a que son mayores que 128, se muestran de manera extraña. | |
8 | Cambie la codificación de la página. de IE a "chino simplificado" "中文" | "中文" (visualización correcta) |
No. | Paso Descripción | Resultado | |
1 | Escriba el archivo fuente JSP y guárdelo en formato GB2312 | D6 D0 CE C4 (D6D0=中文 CEC4=文) | |
2 | jspc convierte el archivo fuente JSP en un archivo JAVA temporal, asigna la cadena a Unicode de acuerdo con ISO8859-1 y la escribe en el archivo JAVA en formato UTF | C3 96 C3 90 C3 8E C3 | |
84 | 3 | El archivo JAVA temporal se compila en un archivo CLASS | C3 96 C3 90 C3 8E C3 84 |
4. | Cuando se ejecuta, primero lea la cadena del archivo CLASS usando readUTF. La codificación Unicode en la memoria es | 00 D6 00 D0 00 CE 00 C4. | |
5. | De acuerdo con Jspcharset=ISO8859-1 Convertir Unicode en un flujo de bytes | D6 D0 CE C4 | |
6 | Enviar el flujo de bytes a IE | D6 D0 CE C4 | |
7 | IE usa la codificación de la página cuando se realiza la solicitud para ver los resultados, | dependiendo de la situación. Si es chino simplificado, se puede mostrar correctamente. De lo contrario, se debe realizar el paso 8 de la Tabla 5. |
No. | Paso Descripción | Resultado |
1 | Escriba el archivo fuente del servlet y guárdelo en formato GB2312 | D6 D0 CE C4 (D6D0=chino CEC4=chino) |
2 | Utilice javac –codificación GB2312 para compilar el archivo fuente JAVA en un archivo CLASS | E4 B8 AD E6 96 87 (UTF) |
3 | Al ejecutar, primero lea la cadena del archivo CLASS usando readUTF y almacene en la memoria La codificación es Unicode | 4E 2D 65 87 (Unicode) |
4 | Convierta Unicode en flujo de bytes | D6 D0 CE C4 (GB2312) | de acuerdo con Servlet-charset=GB2312
5 | Envíe el flujo de bytes a IE y establezca el atributo de codificación de IE en Servlet-charset=GB2312 | D6 D0 CE C4 (GB2312) |
6 | IE usa "chino simplificado" para ver el resultado | "chino" (se muestra correctamente) |
No. | Paso Descripción | Resultado |
1 | Escriba el archivo fuente del servlet y guárdelo en formato GB2312 | D6 D0 CE C4 (D6D0=中文 CEC4=文) |
2 | Utilice javac –codificación ISO8859-1 para compilar el archivo fuente JAVA en un archivo CLASS | C3 96 C3 90 C3 8E C3 84 (UTF) |
3 | Al ejecutar, primero lea la cadena del archivo CLASS usando readUTF, la codificación Unicode en la memoria es | 00 D6 00 D0 00 CE 00 C4 |
4 | Convierta Unicode en un flujo de bytes de acuerdo con Servlet-charset=ISO8859-1 | D6 D0 CE C4 |
5 | Envíe el flujo de bytes a IE y configure la codificación de IE El atributo es Servlet-charset=ISO8859-1 | D6 D0 CE C4 (GB2312) |
6 | IE usa "caracteres de Europa occidental" para ver | resultados confusos (el motivo es el mismo que en la Tabla 5) |
7 | Cambie la codificación de página de IE a "chino simplificado " | "Chino" (se muestra correctamente) |
Descripción del paso | del número de serie | Campo | de resultado |
1 | Ingrese "chino" en IE | D6 D0 CE C4 | IE |
2 | IE convierte la cadena a UTF y la envía al flujo de transporte | E4 B8 AD E6 96 87 | |
3 | El servlet recibe el flujo de entrada y lo lee con readUTF | 4E 2D 65 87 (unicode) | Servlet |
4 | En el Servlet, el programador debe restaurar la cadena en un flujo de bytes | D6 D0 CE C4 | según GB2312|
5 | El programador genera una nueva cadena | 00 D6 00 D0 00 CE | según el código interno de la base de datos ISO8859. -1.00 C4 |
6 | Envíe la cadena recién generada a JDBC | 00 D6 00 D0 00 CE 00 C4 | |
7 | JDBC detecta que el código interno de la base de datos es ISO8859-1 | 00 D6 00 D0 00 CE 00 C4 | JDBC |
8 | JDBC convierte la cadena recibida de acuerdo con ISO8859 -1 Generar flujo de bytes | D6 D0 CE C4 | |
9 | JDBC escribe el flujo de bytes en la base de datos | D6 D0 CE C4 | |
10 | Completa el trabajo de almacenamiento de datos | D6 D0 CE C4 Base de datos | |
El siguiente es el proceso de recuperación de números de la base de datos | |||
11 | JDBC recupera palabras de la base de datos Throttle | D6 D0 CE C4 | JDBC |
12 | JDBC genera una cadena de acuerdo con el juego de caracteres de la base de datos ISO8859-1 y la envía al Servlet | 00 D6 00 D0 00 CE 00 C4 (Unicode) | |
13 | El servlet obtiene la cadena | 00 D6 00 D0 00 CE 00 C4 (Unicode) | Servlet |
14 | El programador debe restaurar el flujo de bytes original | D6 D0 CE C4 | según el código interno ISO8859-1 de la base de datos.|
15 | Los programadores deben generar nuevas cadenas de acuerdo con el juego de caracteres del cliente GB2312 | 4E 2D 65 87 (Unicode) | |
El servlet se prepara para enviar la cadena al cliente | |||
16. | El servlet genera el flujo de bytes | D6D0 CE C4 | Servlet |
17 | basado en <Servlet-charset>. El servlet envía el flujo de bytes a IE. También se configurará el flujo de bytes de IE. La codificación es <Servlet-charset> | D6 D0 CE C4 | |
18 | IE. Vea el resultado"chino" (se muestra correctamente) | de acuerdo con la codificación especificada o la codificación predeterminada | de IE. |