Em primeiro lugar, deixe-me explicar que isso se refere a String em Java. Embora eu tenha decidido mudar para C/C++, porque encontrei um problema hoje, ainda quero dar uma olhada. A definição de String é a seguinte:
Copie o código do código da seguinte forma:
classe final pública String
{
valor de char final privado[]; // string salva
private final int offset; // posição inicial
contagem int final privada; //Número de caracteres
private int hash; // valor de hash armazenado em cache
...
}
Ao depurar, você pode ver os valores salvos da seguinte forma:
Deve-se observar que se hashCode() não tiver sido chamado, o valor do hash será 0. É fácil saber que o valor aqui é a matriz de caracteres do valor real da string salva (ou seja, o "teste de string"), e qual é o valor de cada caractere? Fácil de verificar: Unicode.
Neste ponto, todos podem adivinhar como nossa subString comumente usada é implementada: Se fôssemos implementá-la, deixaríamos a nova String usar o mesmo valor (matriz de caracteres) e modificar apenas o deslocamento e a contagem. Isso economiza espaço e é rápido (não precisa copiar), e na verdade é assim:
Copie o código do código da seguinte forma:
public String substring(int inícioIndex) {
retornar substring(beginIndex, contagem);
}
public String substring(int inícioIndex, int endIndex) {
...
retornar ((beginIndex == 0) && (endIndex == contagem)) isto:
nova String(deslocamento + inícioIndex, endIndex - inícioIndex, valor);
}
String(deslocamento interno, contagem interna, valor char[]) {
este.valor = valor;
this.offset = deslocamento;
isto.contagem = contagem;
}
Já que estamos discutindo strings, qual codificação a JVM usa por padrão? Através da depuração você pode encontrar:
Copie o código do código da seguinte forma:
conjunto de caracteres estático público defaultCharset() {
if (defaultCharset == null) {
sincronizado (Charset.class) {
java.security.PrivilegedAction pa = new GetPropertyAction("arquivo.encoding");
String csn = (String)AccessController.doPrivileged(pa);
Conjunto de caracteres cs = pesquisa(csn);
se (cs! = nulo)
conjunto de caracteres padrão = cs;
outro
defaultCharset = forNome("UTF-8");
}
}
O valor de defaultCharset pode ser passado:
-Dfile.encoding=utf-8
Faça configurações. Claro, se quiser configurá-lo como "abc", você pode, mas será definido como UTF-8 por padrão. Você pode ver o valor específico através de System.getProperty("file.encoding"). Por que você vê defaultCharset? Como o processo de transmissão da rede deve ser composto por matrizes de bytes, as matrizes de bytes obtidas por diferentes métodos de codificação podem ser diferentes. Então, precisamos saber como é obtido o método de codificação, certo? O método específico para obter a matriz de bytes é getBytes, no qual nos concentraremos a seguir. O que ele chama é o método encode de CharsetEncoder, como segue:
Copie o código do código da seguinte forma:
public final CoderResult encode(CharBuffer in, ByteBuffer out, boolean endOfInput) {
int novoEstado = endOfInput ? ST_END : ST_CODING;
if ((estado! = ST_RESET) && (estado! = ST_CODING) &&!(endOfInput && (estado == ST_END)))
throwIllegalStateException(estado, novoEstado);
estado = novoEstado;
para (;;) {
CoderResult cr;
tentar {
cr = encodeLoop(entrada, saída);
} catch (BufferUnderflowException x) {
lançar novo CoderMalfunctionError(x);
} catch (BufferOverflowException x) {
lançar novo CoderMalfunctionError(x);
}
se (cr.isOverflow())
retornar cr;
if (cr.isUnderflow()) {
if (endOfInput && in.hasRemaining()) {
cr = CoderResult.malformedForLength(in.remaining());
} outro {
retornar cr;
}
}
CodingErrorAction ação = null;
if (cr.isMalformado())
ação = malformadaInputAction;
senão se (cr.isUnmappable())
ação = não mapeávelCharacterAction;
outro
afirmar falso: cr.toString();
if (ação == CodingErrorAction.REPORT)
retornar cr;
if (ação == CodingErrorAction.REPLACE) {
if (out.remaining() <substituição.comprimento)
retornar CoderResult.OVERFLOW;
saída.put(substituição);
}
if ((ação == CodingErrorAction.IGNORE) || (ação == CodingErrorAction.REPLACE)) {
in.position(in.position() + cr.length());
continuar;
}
afirmar falso;
}
}
Obviamente, o CharsetEncoder correspondente será selecionado primeiro de acordo com o formato de codificação necessário, e o mais importante é que diferentes CharsetEncoder implementem diferentes métodos encodeLoop. Você pode não entender por que existe um for(;;) aqui? Na verdade, você pode entendê-lo aproximadamente olhando o pacote (nio) onde CharsetEncoder está localizado e seus parâmetros: esta função pode lidar com fluxos (embora não faremos loop quando a usarmos aqui).
No método encodeLoop, tantos caracteres quanto possível serão convertidos em bytes, e a nova String é quase o processo inverso acima.
No processo de desenvolvimento real, caracteres distorcidos são frequentemente encontrados:
Obtenha o nome do arquivo ao fazer upload do arquivo;
A string passada por JS para o backend;
Primeiro tente os resultados de execução do seguinte código:
Copie o código do código da seguinte forma:
public static void main(String[] args) lança exceção {
Stringstr = "string";
// -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"));
//Personagem??
System.out.println(new String("瀛涓?".getBytes("gbk"), "utf-8"));
// -41 -42 -73 -5 63 63
printArray(new String("Yingjuan?".getBytes("gbk"), "utf-8").getBytes());
}
public static void printArray(byte[] bs){
for(int i = 0; i < bs.length; i++){
System.out.print(bs[i] + " ");
}
System.out.println();
}
A saída está descrita nos comentários do programa:
Como 2 bytes em GBK representam um caractere chinês, existem 6 bytes;
Como 3 bytes em UTF-8 representam um caractere chinês, existem 9 bytes;
Como a matriz de bytes que não pode ser gerada pelo GBK é usada para gerar uma string de acordo com as regras UTF-8, ??? é exibido;
Esta é a razão pela qual caracteres ilegíveis são frequentemente encontrados. GBK usa o byte gerado por UTF-8 para gerar strings;
Embora o código gerado acima esteja distorcido, o computador não pensa assim, então ele ainda pode obter a matriz de bytes por meio de getBytes, e o UTF-8 nesta matriz pode ser reconhecido;
Os dois últimos 63 (?) devem ser preenchidos por encode (ou não há bytes suficientes para preencher diretamente, não olhei atentamente para este local);
Como a codificação de letras e números é a mesma entre GBK e UTF-8, não haverá caracteres distorcidos no processamento desses caracteres. No entanto, a codificação de caracteres chineses é realmente diferente. no código abaixo:
new String(new String("nós".getBytes("UTF-8"), "GBK").getBytes("GBK"), "UTF-8);
Obviamente o resultado deste código é “nós”, mas o que ele faz conosco? Primeiro notamos:
new String("nós".getBytes("UTF-8"), "GBK");
O resultado desse código é um código distorcido, e muitos códigos distorcidos são "confusos assim". Mas lembre-se: o caos aqui é para nós, e para o computador, não importa se é “bagunçado” ou “não bagunçado”. Quando quase desistimos, ele ainda pode obtê-lo a partir do código ilegível através de “getBytes(”. "GBK")” É “backbone”, e então podemos usar o “backbone” para restaurar a string original.
Parece que o código acima pode resolver o problema distorcido entre "GBK" e "UTF-8", mas esta solução é limitada apenas a um caso especial: o número de todos os caracteres chineses consecutivos é um número par! As razões foram mencionadas acima e não serão repetidas aqui.
Então, como resolver esse problema?
A primeira solução: encodeURI Por que usar este método? A razão é muito simples: GBK e UTF-8 têm a mesma codificação de%, números e letras, então a string após a codificação pode ser 100% garantida como a mesma coisa nessas duas codificações e, em seguida, decodificar para obter os caracteres . Só espeto. De acordo com o formato String, podemos adivinhar que a eficiência de codificação e decodificação é muito, muito alta, então esta também é uma boa solução.
A segunda solução: Formato de codificação unificado <BR>Estamos usando mineração Webx aqui. Você só precisa definir defaultCharset="UTF-8" em webx.xml.