Tout d'abord, laissez-moi vous expliquer que cela fait référence à String en Java. Bien que j'aie décidé de passer au C/C++, car j'ai rencontré un problème aujourd'hui, je souhaite quand même y jeter un œil. La définition de String est la suivante :
Copiez le code comme suit :
chaîne de classe finale publique
{
valeur de caractère finale privée [] ; // chaîne enregistrée
private final int offset ; // position de départ
nombre int final privé ; //Nombre de caractères
hachage int privé ; // valeur de hachage mise en cache
...
}
Lors du débogage, vous pouvez voir les valeurs enregistrées comme suit :
Il convient de noter que si hashCode() n'a pas été appelé, la valeur de hachage est 0. Il est facile de savoir que la valeur ici est le tableau de caractères de la valeur de chaîne réellement enregistrée (c'est-à-dire le "test de chaîne"), et quelle est la valeur de chaque caractère ? Facile à vérifier : Unicode.
À ce stade, tout le monde peut deviner comment notre subString couramment utilisée est implémentée : si nous devions l'implémenter, laissez la nouvelle chaîne utiliser la même valeur (tableau de caractères) et modifiez uniquement le décalage et le nombre. Cela économise de l'espace et est rapide (pas besoin de copier), et en fait c'est comme ça :
Copiez le code comme suit :
sous-chaîne de chaîne publique (int startIndex) {
retourner la sous-chaîne (beginIndex, count);
}
sous-chaîne de chaîne publique (int startIndex, int endIndex) {
...
return ((beginIndex == 0) && (endIndex == count)) ceci :
new String (offset +beginIndex,endIndex -beginIndex,valeur);
}
String(int offset, int count, char value[]) {
this.value = valeur ;
this.offset = décalage ;
this.count = nombre ;
}
Puisque nous parlons de chaînes, quel encodage la JVM utilise-t-elle par défaut ? Grâce au débogage, vous pouvez trouver :
Copiez le code comme suit :
jeu de caractères statique public defaultCharset() {
si (defaultCharset == null) {
synchronisé (Charset.class) {
java.security.PrivilegedAction pa = new GetPropertyAction("file.encoding");
Chaîne csn = (String)AccessController.doPrivileged(pa);
Jeu de caractères cs = recherche (csn);
si (cs != nul)
defaultCharset = cs ;
autre
defaultCharset = forName("UTF-8");
}
}
La valeur de defaultCharset peut être transmise :
-Dfile.encoding=utf-8
Effectuez les réglages. Bien sûr, si vous souhaitez le définir sur "abc", vous le pouvez, mais il sera défini sur UTF-8 par défaut. Vous pouvez voir la valeur spécifique via System.getProperty("file.encoding"). Pourquoi voyez-vous defaultCharset ? Étant donné que le processus de transmission réseau doit être constitué de tableaux d'octets, les tableaux d'octets obtenus par différentes méthodes de codage peuvent être différents. Nous devons donc savoir comment la méthode d’encodage est obtenue, n’est-ce pas ? La méthode spécifique pour obtenir le tableau d'octets est getBytes, sur laquelle nous nous concentrerons ci-dessous. Ce qu'elle appelle finalement est la méthode encode de CharsetEncoder, comme suit :
Copiez le code comme suit :
public final CoderResult encode (CharBuffer in, ByteBuffer out, boolean endOfInput) {
int newState = endOfInput ? ST_END : ST_CODING;
if ((state != ST_RESET) && (state != ST_CODING) && !(endOfInput && (state == ST_END)))
throwIllegalStateException(état, newState);
état = nouvelÉtat ;
pour (;;) {
CoderResult cr;
essayer {
cr = encodeLoop(entrée, sortie);
} catch (BufferUnderflowException x) {
lancer un nouveau CoderMalfunctionError(x);
} catch (BufferOverflowException x) {
lancer un nouveau CoderMalfunctionError(x);
}
si (cr.isOverflow())
retourner cr;
si (cr.isUnderflow()) {
if (endOfInput && in.hasRemaining()) {
cr = CoderResult.malformedForLength(in.remaining());
} autre {
retourner cr;
}
}
Action CodingErrorAction = null ;
si (cr.isMalformed())
action = malformedInputAction ;
sinon si (cr.isUnmappable())
action = unmappableCharacterAction ;
autre
assert false : cr.toString();
si (action == CodingErrorAction.REPORT)
retourner cr;
si (action == CodingErrorAction.REPLACE) {
if (out.remaining() < remplacement.length)
retourner CoderResult.OVERFLOW ;
out.put (remplacement);
}
if ((action == CodingErrorAction.IGNORE) || (action == CodingErrorAction.REPLACE)) {
in.position(in.position() + cr.length());
continuer;
}
affirmer faux;
}
}
Bien entendu, le CharsetEncoder correspondant sera d'abord sélectionné en fonction du format d'encodage requis, et le plus important est que différents CharsetEncoder implémentent différentes méthodes encodeLoop. Vous ne comprenez peut-être pas pourquoi il y a un for(;;) ici ? En fait, vous pouvez à peu près le comprendre en regardant le package (nio) où se trouve CharsetEncoder et ses paramètres : cette fonction peut gérer des flux (même si nous ne ferons pas de boucle lorsque nous l'utiliserons ici).
Dans la méthode encodeLoop, autant de caractères que possible seront convertis en octets, et new String est presque le processus inverse ci-dessus.
Dans le processus de développement actuel, des caractères tronqués sont souvent rencontrés :
Obtenez le nom du fichier lors du téléchargement du fichier ;
La chaîne transmise par JS au backend ;
Essayez d’abord les résultats d’exécution du code suivant :
Copiez le code comme suit :
public static void main (String[] args) lève une exception {
Chaîne str = "chaîne" ;
// -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"));
//Personnage??
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){
pour(int i = 0; i < bs.length; i++){
System.out.print(bs[i] + " );
}
System.out.println();
}
Le résultat est décrit dans les commentaires du programme :
Parce que 2 octets dans GBK représentent un caractère chinois, il y a 6 octets ;
Étant donné que 3 octets en UTF-8 représentent un caractère chinois, il y a 9 octets ;
Parce que le tableau d'octets qui ne peut pas être généré par GBK est utilisé pour générer une chaîne selon les règles UTF-8, ??? est affiché ;
C'est la raison pour laquelle des caractères tronqués sont souvent rencontrés. GBK utilise l'octet généré par UTF-8 pour générer des chaînes ;
Bien que le code généré ci-dessus soit tronqué, l'ordinateur ne le pense pas, il peut donc toujours obtenir le tableau d'octets via getBytes, et l'UTF-8 dans ce tableau peut être reconnu ;
Les deux derniers 63 (?) doivent être remplis par encodage (ou il n'y a pas assez d'octets à remplir directement, je n'ai pas regardé attentivement cet endroit) ;
Parce que l'encodage des lettres et des chiffres est le même entre GBK et UTF-8, il n'y aura pas de caractères tronqués dans le traitement de ces caractères. Cependant, leur encodage des caractères chinois est effectivement différent. C'est l'origine de nombreux problèmes. au code ci-dessous :
new String(new String("we".getBytes("UTF-8"), "GBK").getBytes("GBK"), "UTF-8);
Évidemment, le résultat de ce code est « nous », mais que nous fait-il ? On remarque d'abord :
new String("we".getBytes("UTF-8"), "GBK");
Le résultat de ce code est un code tronqué, et de nombreux codes tronqués sont « foirés comme ça ». Mais rappelez-vous : le chaos ici est pour nous, et pour l'ordinateur, peu importe que ce soit « désordonné » ou « pas désordonné ». Lorsque nous abandonnons presque, il peut toujours l'obtenir à partir du code tronqué via « getBytes( ». "GBK")" Son "backbone", et nous pouvons ensuite utiliser le "backbone" pour restaurer la chaîne d'origine.
Il semble que le code ci-dessus puisse résoudre le problème de confusion entre "GBK" et "UTF-8", mais cette solution n'est limitée qu'à un cas particulier : le nombre de tous les caractères chinois consécutifs est un nombre pair ! Les raisons ont été évoquées ci-dessus et ne seront pas répétées ici.
Alors comment résoudre ce problème ?
La première solution : encodeURI Pourquoi utiliser cette méthode ? La raison est très simple : GBK et UTF-8 ont le même encodage de %, de chiffres et de lettres, donc la chaîne après encodage peut être garantie à 100 % qu'elle est la même chose sous ces deux encodages, puis décodée pour obtenir les caractères. .Juste une brochette. D'après le format de String, on peut deviner que l'efficacité de l'encodage et du décodage est très, très élevée, c'est donc aussi une bonne solution.
La deuxième solution : Format d'encodage unifié <BR>Nous utilisons ici le minage Webx. Il vous suffit de définir defaultCharset="UTF-8" dans webx.xml.