En primer lugar, permítanme explicarles que esto se refiere a String en Java. Aunque decidí cambiar a C/C++, debido a que encontré un problema hoy, todavía quiero echarle un vistazo. La definición de String es la siguiente:
Copie el código de código de la siguiente manera:
Cadena de clase final pública
{
valor de carácter final privado [] // cadena guardada
desplazamiento int final privado // posición inicial
recuento int final privado; //Número de caracteres
hash int privado; // valor hash almacenado en caché
...
}
Al depurar, puede ver los valores guardados de la siguiente manera:
Cabe señalar que si no se ha llamado a hashCode(), el valor hash es 0. Es fácil saber que el valor aquí es la matriz de caracteres del valor de cadena guardado real (es decir, la "prueba de cadena"), y ¿cuál es el valor de cada carácter? Fácil de verificar: Unicode.
En este punto, todos pueden adivinar cómo se implementa nuestra subCadena comúnmente utilizada: si la implementáramos, dejaríamos que la nueva Cadena use el mismo valor (matriz de caracteres) y solo modifiquemos el desplazamiento y el recuento. Esto ahorra espacio y es rápido (no es necesario copiar), y de hecho es así:
Copie el código de código de la siguiente manera:
subcadena de cadena pública (int comenzarIndex) {
devolver subcadena (beginIndex, contar);
}
subcadena de cadena pública (int startIndex, int endIndex) {
...
return ((beginIndex == 0) && (endIndex == recuento)) esto:
nueva cadena (desplazamiento + comenzarIndex, endIndex - comenzarIndex, valor);
}
Cadena (int desplazamiento, int recuento, valor de carácter []) {
this.value = valor;
this.offset = desplazamiento;
this.count = contar;
}
Ya que estamos hablando de cadenas, ¿qué codificación utiliza la JVM de forma predeterminada? A través de la depuración puedes encontrar:
Copie el código de código de la siguiente manera:
conjunto de caracteres estático público defaultCharset() {
si (defaultCharset == nulo) {
sincronizado (Charset.class) {
java.security.PrivilegedAction pa = new GetPropertyAction("file.encoding");
Cadena csn = (Cadena)AccessController.doPrivileged(pa);
Conjunto de caracteres cs = búsqueda(csn);
si (cs! = nulo)
defaultCharset = cs;
demás
defaultCharset = forName("UTF-8");
}
}
El valor de defaultCharset se puede pasar:
-Dfile.codificación=utf-8
Realizar ajustes. Por supuesto, si desea configurarlo en "abc", puede hacerlo, pero estará configurado en UTF-8 de forma predeterminada. Puede ver el valor específico a través de System.getProperty ("file.encoding"). ¿Por qué ves defaultCharset? Debido a que el proceso de transmisión de la red debe ser una matriz de bytes, las matrices de bytes obtenidas mediante diferentes métodos de codificación pueden ser diferentes. Entonces, necesitamos saber cómo se obtiene el método de codificación, ¿verdad? El método específico para obtener la matriz de bytes es getBytes, en el que nos centraremos a continuación. Lo que finalmente llama es el método de codificación de CharsetEncoder, de la siguiente manera:
Copie el código de código de la siguiente manera:
codificación pública final de CoderResult (entrada CharBuffer, salida ByteBuffer, final booleano de entrada) {
int nuevoEstado = endOfInput? ST_END: ST_CODING;
if ((estado!= ST_RESET) && (estado!= ST_CODING) &&!(endOfInput && (estado == ST_END)))
throwIllegalStateException(estado, nuevoEstado);
estado = nuevoEstado;
para (;;) {
CoderResult cr;
intentar {
cr = codificarLoop(entrada, salida);
} captura (BufferUnderflowException x) {
lanzar nuevo CoderMalfunctionError(x);
} captura (BufferOverflowException x) {
lanzar nuevo CoderMalfunctionError(x);
}
si (cr.isOverflow())
volver cr;
si (cr.isUnderflow()) {
si (endOfInput && en.hasRemaining()) {
cr = CoderResult.malformedForLength(in.remaining());
} demás {
volver cr;
}
}
Acción de codificaciónErrorAction = nulo;
si (cr.isMalformed())
acción = malformadoInputAction;
de lo contrario si (cr.isUnmappable())
acción = acción de carácter no asignable;
demás
afirmar falso: cr.toString();
si (acción == CodingErrorAction.REPORT)
volver cr;
if (acción == CodingErrorAction.REPLACE) {
if (out.restante() < reemplazo.longitud)
devolver CoderResult.OVERFLOW;
salida.salida(reemplazo);
}
if ((acción == CodingErrorAction.IGNORE) || (acción == CodingErrorAction.REPLACE)) {
en.posición(en.posición() + cr.longitud());
continuar;
}
afirmar falso;
}
}
Por supuesto, el CharsetEncoder correspondiente se seleccionará primero de acuerdo con el formato de codificación requerido, y lo más importante es que diferentes CharsetEncoder implementan diferentes métodos encodeLoop. Quizás no entiendas por qué hay un for (;;) aquí. De hecho, puedes entenderlo aproximadamente mirando el paquete (nio) donde se encuentra CharsetEncoder y sus parámetros: esta función puede manejar transmisiones (aunque no realizaremos un bucle cuando la usemos aquí).
En el método encodeLoop, se convertirán tantos caracteres como sea posible en bytes, y la nueva cadena es casi el proceso inverso anterior.
En el proceso de desarrollo real, a menudo se encuentran caracteres confusos:
Obtenga el nombre del archivo al cargar el archivo;
La cadena pasada por JS al backend;
Primero pruebe los resultados de ejecución del siguiente código:
Copie el código de código de la siguiente manera:
public static void main (String [] args) lanza una excepción {
Cadena cadena = "cadena";
// -41 -42 -73 -5 -76 -82
printArray(str.getBytes());
// -27 -83 -105 -25 -84 -90 -28 -72 -78
printArray(str.getBytes("utf-8"));
// ???
System.out.println(new String(str.getBytes(), "utf-8"));
// ¿Yingjuan?
System.out.println(new String(str.getBytes("utf-8"), "gbk"));
//¿¿Personaje??
System.out.println(new String("瀛涓?".getBytes("gbk"), "utf-8"));
// -41 -42 -73 -5 63 63
printArray(new String("Yingjuan?".getBytes("gbk"), "utf-8").getBytes());
}
printArray vacío estático público (byte [] bs) {
for(int i = 0; i < bs.length; i++){
System.out.print(bs[i] + " ");
}
Sistema.out.println();
}
El resultado se describe en los comentarios del programa:
Debido a que 2 bytes en GBK representan un carácter chino, hay 6 bytes;
Debido a que 3 bytes en UTF-8 representan un carácter chino, hay 9 bytes;
Debido a que la matriz de bytes que GBK no puede generar se utiliza para generar una cadena de acuerdo con las reglas UTF-8, se muestra ???
Esta es la razón por la que a menudo se encuentran caracteres confusos. GBK utiliza bytes generados por UTF-8 para generar cadenas;
Aunque el código generado anteriormente está confuso, la computadora no lo cree así, por lo que aún puede obtener la matriz de bytes a través de getBytes, y se puede reconocer UTF-8 en esta matriz;
Los dos últimos 63 (?) deben completarse mediante codificación (o no hay suficientes bytes para completar directamente, no miré detenidamente este lugar);
Debido a que la codificación de letras y números es la misma entre GBK y UTF-8, no habrá caracteres confusos en el procesamiento de estos caracteres. Sin embargo, la codificación de los caracteres chinos es realmente diferente. Este es el origen de muchos problemas. en el siguiente código:
nueva cadena(nueva cadena("nosotros".getBytes("UTF-8"), "GBK").getBytes("GBK"), "UTF-8);
Obviamente el resultado de este código es "nosotros", pero ¿qué nos hace? Primero notamos:
nueva cadena("nosotros".getBytes("UTF-8"), "GBK");
El resultado de este código es un código confuso, y muchos códigos confusos están "estropeados así". Pero recuerde: el caos aquí es para nosotros y para la computadora, no importa si es "desordenado" o "no desordenado". Cuando casi nos damos por vencidos, aún puede obtenerlo del código confuso a través de "getBytes(". "GBK")” Su "columna vertebral", y luego podemos usar la "columna vertebral" para restaurar la cadena original.
Parece que el código anterior puede resolver el problema confuso entre "GBK" y "UTF-8", pero esta solución solo se limita a un caso especial: ¡el número de todos los caracteres chinos consecutivos es un número par! Las razones se han mencionado anteriormente y no se repetirán aquí.
Entonces, ¿cómo solucionar este problema?
La primera solución: codificarURI ¿Por qué utilizar este método? La razón es muy simple: GBK y UTF-8 tienen la misma codificación de %, números y letras, por lo que se puede garantizar al 100% que la cadena después de la codificación sea la misma bajo estas dos codificaciones, y luego decodificarla para obtener los caracteres. . Sólo brocheta. Según el formato de String, podemos adivinar que la eficiencia de codificación y decodificación es muy, muy alta, por lo que esta también es una buena solución.
La segunda solución: formato de codificación unificada <BR>Aquí utilizamos minería Webx. Solo necesita configurar defaultCharset="UTF-8" en webx.xml.