1. Préface
En fait, depuis que j'ai commencé à écrire du code Java, j'ai rencontré d'innombrables problèmes brouillés et transcodants, tels que le code brouillé qui se produit lors de la lecture d'un fichier texte dans une chaîne, du code brouillé qui se produit lorsqu'il obtient des paramètres de demande HTTP dans un servlet, un code brouillé qui se produit lorsqu'il est interrogé par JDBC, etc. Ces problèmes sont très courants. Lorsque vous les rencontrez, vous pouvez les résoudre avec succès en les recherchant, afin que vous n'ayez pas de compréhension approfondie.
Jusqu'à il y a deux jours, mon camarade de classe m'a parlé d'un problème de codage de fichier source Java (ce problème est analysé dans le dernier exemple), et a commencé avec ce problème et a commencé par une série de problèmes. Nous avons ensuite discuté en recherchant les informations. Il était tard dans la nuit que nous avons finalement trouvé un indice clé dans un blog, résolvant tous les doutes, et les phrases que nous n'avions pas compris auparavant pourraient être expliquées clairement. Par conséquent, j'ai décidé d'utiliser cet essai pour enregistrer ma compréhension de certains problèmes de codage et des résultats de l'expérience.
Certains des concepts suivants sont ma propre compréhension en fonction des conditions réelles. S'il y a une erreur, assurez-vous de les corriger.
2. Résumé du concept
Au début, Internet ne s'était pas encore développé et les ordinateurs n'ont été utilisés que pour traiter certaines données locales, de nombreux pays et régions ont conçu des schémas de codage pour les langues locales. Ce type de codage régional est collectivement appelé codage ANSI (car ce sont des extensions aux codes ANSIII). Cependant, ils n'ont pas discuté à l'avance de la façon d'être compatible les uns avec les autres, mais ont plutôt fait le leur, ce qui a jeté la racine des conflits d'encodage. Par exemple, le codage GB2312 utilisé dans le continent entre les conflits avec le codage Big5 utilisé à Taïwan. Les deux mêmes octets représentent des caractères différents dans les deux schémas de codage. Avec la montée en puissance d'Internet, un document contient souvent plusieurs langues, et l'ordinateur rencontre des problèmes lors de l'affichage car il ne sait pas à quel codage de ces deux octets appartiennent.
Ces problèmes sont courants dans le monde, donc les appels à la redéfinition d'un jeu de caractères commun et une numérotation unifiée de tous les personnages du monde augmentent.
En conséquence, le code Unicode a vu le jour, il comptait uniformément tous les caractères du monde. Puisqu'il peut identifier de manière unique un caractère, la police doit être conçue uniquement pour le code Unicode. Cependant, la norme Unicode définit un jeu de caractères, mais ne spécifie pas le schéma de codage, c'est-à-dire qu'il définit uniquement les nombres abstraits et les caractères correspondants, mais ne spécifie pas comment stocker une chaîne de numéros Unicode. La vraie exigence est de savoir comment stocker les solutions UTF-8, UTF-16, UTF-32 et d'autres solutions. Par conséquent, les encodages avec les débuts UTF peuvent être directement convertis par des calculs et des valeurs Unicode (points de code, points de code). Comme son nom l'indique, UTF-8 est un codage de longueur de 8 bits, qui est un codage de longueur variable, en utilisant 1 à 6 octets pour coder un caractère (car il est limité par la plage Unicode, il n'est en fait que de 4 octets au maximum); UTF-16 est un codage d'unité de base 16 bits, qui est également un codage de longueur variable, soit 2 octets ou 4 octets; L'UTF-32 est une longueur fixe et une fixe de 4 octets stockent un numéro Unicode.
En fait, j'ai toujours été un peu incompréhensible à propos de Unicode auparavant. Dans mon impression, le code Unicode ne peut atteindre que 0xffff, ce qui signifie qu'il ne peut représenter que jusqu'à 2 ^ 16 caractères. Après avoir soigneusement lu Wikipedia, j'ai réalisé que le premier schéma d'encodage UCS-2 était en effet comme ça. UCS-2 a utilisé deux octets pour coder un caractère, il ne peut donc coder les caractères que dans la plage de BMP (plan multilingue de base, c'est-à-dire 0x0000-0xffff, qui contient les caractères les plus couramment utilisés au monde). Afin de coder les caractères avec Unicode supérieur à 0xFFFF, les gens ont élargi le codage UCS-2 et créé le codage UTF-16, qui est de longueur variable. Dans la gamme BMP, l'UTF-16 est exactement la même que UCS-2, tandis que l'UTF-16 en dehors du BMP utilise 4 octets pour stocker.
Pour faciliter la description ci-dessous, permettez-moi d'expliquer le concept d'unité de code (CodeUnit). Le composant de base d'un certain codage est appelé l'unité de code. Par exemple, l'unité de code de l'UTF-8 est de 1 octet, et l'unité de code de l'UTF-16 est de 2 octets. C'est difficile à expliquer, mais c'est facile à comprendre.
Afin d'être compatible avec diverses langues et une meilleure plate-forme multiplateuse, Javastring enregistre le code Unicode pour les caractères. Il utilisait le schéma d'encodage UCS-2 pour stocker Unicode. Plus tard, il a constaté que les caractères de la gamme BMP n'étaient pas suffisants, mais pour les considérations de consommation de mémoire et de compatibilité, il n'a pas atteint l'UCS-4 (c'est-à-dire UTF-32, codage fixe de 4 octets), mais a adopté l'UTF-16 mentionné ci-dessus. Le type de char peut être considéré comme son unité de code. Cette pratique cause des ennuis. Si tous les caractères sont dans la gamme BMP, c'est bien. S'il y a des caractères en dehors de BMP, ce n'est plus une unité de code correspondant à un caractère. La méthode de longueur renvoie le nombre d'unités de code, pas le nombre de caractères. La méthode Charat renvoie naturellement une unité de code au lieu d'un caractère, qui devient gênant lors de la traversée. Bien que certaines nouvelles méthodes d'opération soient fournies, elle est toujours gênante et ne peut être accessible au hasard.
In addition, I found that Java does not process Unicode literals larger than 0xFFFF when compiling, so if you can't type a non-BMP character, but you know its Unicode code, you have to use a relatively stupid method to let String store it: manually calculate the UTF-16 encoding (four bytes) of the character, and use the first two bytes and the last two bytes as a Unicode number, and then assign the value to String. L'exemple de code est le suivant.
public static void main (String [] args) {// String str = ""; // Nous voulons attribuer un tel caractère, en supposant que ma méthode d'entrée ne peut pas être tapée // mais je sais que son Unicode est 0x1d11e // String str = "/ u1d11e"; // Ceci ne sera pas reconnu // donc il peut être calculé via le codage UTF-16 D834 DD1ESTRING STR = "/ UD834 / UDD1E"; // Ecrire System.out.println (str); // Sortie avec succès ""}Le bloc-notes fourni avec Windows peut être enregistré en codage Unicode, qui fait réellement référence au codage UTF-16. Comme mentionné ci-dessus, les principaux codages de caractères utilisés sont tous dans la plage BMP, et dans la plage de BMP, la valeur de codage UTF-16 de chaque caractère est égale à la valeur Unicode correspondante, c'est probablement pourquoi Microsoft l'appelle Unicode. Par exemple, je suis entré dans les deux caractères "Good A" dans le bloc-notes, puis je l'ai enregistré en codage Unicode Big Endian (priorité élevée), et j'ai ouvert le fichier avec Winhex. Le contenu est comme indiqué dans la figure ci-dessous. Les deux premiers octets du fichier sont appelés la marque d'ordre des octets (Mark d'ordre des octets), (Fe FF) marque l'ordre endian en tant que priorité élevée, puis (59 7D) est le "bon" code Unicode, et (00 61) est le code "Unicode" un ".
Avec le code Unicode, le problème ne peut pas être résolu immédiatement, car tout d'abord, il y a une grande quantité de données de codage standard non Unicode dans le monde, et il nous est impossible de les jeter. Deuxièmement, le codage Unicode prend souvent plus d'espace que le codage ANSI, donc du point de vue de l'épargne des ressources, le codage ANSI est toujours nécessaire. Par conséquent, il est nécessaire d'établir un mécanisme de conversion afin que le codage ANSI puisse être converti en Unicode pour le traitement unifié, ou Unicode peut être converti en codage ANSI pour répondre aux exigences de la plate-forme.
La méthode de conversion est relativement facile à dire. Pour les séries UTF ou ISO-8859-1, les codages compatibles peuvent être directement convertis par le calcul et les valeurs Unicode (en fait, il peut également s'agir d'une recherche de table). Pour le codage ANSI laissé du système, cela ne peut être fait qu'en recherchant la table. Microsoft appelle ce codepage de table de mappage (page de code) et classe et numéroté par codage. Par exemple, notre CP936 commun est la page de code GBK, et CP65001 est la page de code UTF-8. Le chiffre suivant est le tableau de mappage GBK-> Unicode trouvé sur le site officiel de Microsoft (visuellement incomplet). De même, il devrait y avoir une table de mappage Unicode-> GBK inversée.
Avec une page de code, vous pouvez facilement effectuer diverses conversions de codage. Par exemple, convertissant de GBK en UTF-8, il vous suffit de diviser les données par des caractères en fonction des règles de codage GBK, d'utiliser les données codées de chaque caractère pour vérifier la page de code GBK, d'obtenir sa valeur Unicode, puis d'utiliser le UNICODE pour vérifier le codage UTF-8 (ou de calculer directement), et vous pouvez obtenir le codage UTF-8 correspondant. Il en va de même pour l'inverse. Remarque: UTF-8 est une implémentation standard d'Unicode. Sa page de code contient toutes les valeurs Unicode, de sorte que tout encodage est converti en UTF-8 puis converti ne sera pas perdu. À ce stade, nous pouvons tirer une conclusion selon laquelle pour terminer le travail de conversion de codage, la chose la plus importante est de convertir avec succès en Unicode, donc le choix correct du jeu de caractères (page de code) est la clé.
Après avoir compris la nature du problème de perte de transcodage, j'ai soudainement compris pourquoi le cadre JSP a utilisé ISO-8859-1 pour décoder les paramètres de demande HTTP, ce qui a conduit au fait que nous devions écrire de telles déclarations lorsque nous avons obtenu des paramètres chinois:
Stringparam=newString(s.getBytes("iso-8859-1"),"UTF-8");
Étant donné que le framework JSP reçoit un flux d'octets binaires codé par le paramètre, il ne sait pas quel codage il est (ou s'en soucie), et il ne sait pas quelle page de code à vérifier pour convertir en Unicode. Ensuite, il a choisi une solution qui ne provoquera jamais de perte. Il suppose qu'il s'agit des données codées par ISO-8859-1, puis recherche la page de code ISO-8859-1 pour obtenir la séquence Unicode. Parce que ISO-8859-1 est codé par des octets, et contrairement à ASCII, il code chaque bit d'espace 0 ~ 255, de sorte que n'importe quel octet peut être trouvé dans sa page de code. S'il est tourné d'Unicode au flux d'octet d'origine, il n'y aura pas de perte. De cette façon, pour les programmeurs européens et américains qui ne considèrent pas d'autres langues, ils peuvent décoder directement la chaîne avec le cadre JSP. S'ils veulent être compatibles avec d'autres langues, ils n'ont qu'à revenir au flux d'octets d'origine et à le décoder avec la page de code réelle.
J'ai fini d'expliquer les concepts connexes d'unicode et de codage de caractères. Ensuite, j'utiliserai des exemples Java pour en faire l'expérience.
Iii. Exemple d'analyse
1. Convertir en constructeur de cordes Unicode
La méthode de construction de la chaîne est de convertir diverses données codées en une séquence Unicode (stockée dans le codage UTF-16). Le code de test suivant est utilisé pour afficher l'application de la méthode de construction Javastrring. Les caractères non BMP sont impliqués dans les exemples, de sorte que les méthodes CodePointat ne sont pas utilisées.
Test de classe publique {public static void main (String [] args) lance ioException {// "Hello" GBK Données codées BYTE [] gbkdata = {(byte) 0xc4, (byte) 0xe3, (byte) 0xba, (byte) 0xc3}; // "Hello" big5 codé de données Byte [] big5data = {(byte) 0xa7, (byte) 0x41, (byte) 0xa6, (byte) 0x6e}; // Construire la chaîne et la décoder à Unicodestring strfromgbk = new String (gbkdata, "gbk"); string strfromb5 = new String (big5data, "big5"); //putant unicode femences respectivement. showUnicode (strFromgbk); showunicode (strFrombig5);} public static void showunicode (string str) {for (int i = 0; i <str.length (); i ++) {System.out.printf ("// u% x", (int) str.charat (i));} system.out.println ();}}Les résultats de l'opération sont les suivants
On peut constater que depuis le code Unicode de String Masters, il doit être converti en autres encodages SOEASY!
3. Utiliser Unicode comme pont pour réaliser la conversion mutuelle codante
Avec le fondement des deux parties ci-dessus, il est très simple de réaliser le codage et la conversion mutuelle. Il vous suffit de les utiliser ensemble. Tout d'abord, la nouvelle convertit les données codées d'origine en une séquence Unicode, puis appelle les Getbytes pour transférer au codage spécifié.
Par exemple, un code de conversion GBK vers BIG5 très simple est le suivant
public static void main (String [] args) lève un non soutenu String (gbkdata, "gbk"); // converti de Unicode en byte de codage big5 [] big5data = tmp.getbytes ("big5"); // deuxième opérations ...}4. Problème de perte de codage
Comme expliqué ci-dessus, la raison pour laquelle le framework JSP utilise le jeu de caractères ISO-8859-1 pour le décoder. Utilisez d'abord un exemple pour simuler ce processus de restauration, le code est le suivant
Test de classe publique {public static void main (String [] args) lève un framework UnsupportDencodingException {// jsp reçoit 6 octets de données byte [] data = {(byte) 0xe4, (byte) 0xbd, (byte) 0xa0, (byte) 0xe5, (byte) 0xa5, (byte) 0xbd}; showBytes (data); // jsp framework suppose qu'il s'agit de l'encodage ISO-8859-1, génère une chaîne d'objet de chaîne tmp = new String (data, "iso-8859-1"); // **********************************. Résultat de décodage: "+ tmp); // donc d'abord les 6 octets d'origine des données (recherchez réversement la page de code d'ISO-8859-1) octet [] utfdata = tmp.getBytes (" ISO-8859-1 "); // Impriment les données restaurées ShowBytes (utfdata); // le développeur sait UTF-8 pour reconstruire le résultat de la chaîne de chaîne Résultat = new String (utfdata, "utf-8"); // imprimez à nouveau, c'est correct! System.out.println ("Résultat de décodage UTF-8:" + Résultat);} public static void showBytes (byte [] data) {for (byte b: data) System.out.printf ("0x% x", b); System.out.println ();}}Le résultat en cours d'exécution est le suivant. La première sortie est incorrecte car les règles de décodage sont incorrectes. J'ai également vérifié la page de codes de manière incorrecte et j'ai eu le mauvais Unicode. Ensuite, j'ai constaté que les données peuvent être parfaitement restaurées via la mauvaise vérification du dos Unicode de la page de code ISO-8859-1.
Ce n'est pas le point. Si la clé est de remplacer "Chine" par "Chine", la compilation sera réussie et le résultat de l'opération est comme indiqué dans la figure ci-dessous. De plus, on peut constater en outre que lorsque le nombre de caractères chinois est impair, la compilation échoue et lorsque le nombre est uniforme, il passe. Pourquoi est-ce? Analyons-le en détail ci-dessous.
Étant donné que JavaString utilise Unicode en interne, le compilateur transcodera nos littéraux de chaîne pendant la compilation et se convertira à partir du codage du fichier source en Unicode (Wikipedia dit qu'il utilise un codage légèrement différent de UTF-8). Lors de la compilation, nous n'avons pas spécifié le paramètre de codage, le compilateur le décodera donc par défaut dans GBK. Si vous avez une certaine connaissance de l'UTF-8 et GBK, vous devez savoir que généralement un caractère chinois a besoin de 3 octets pour utiliser le codage UTF-8, tandis que GBK n'a besoin que de 2 octets. Cela peut expliquer pourquoi la parité du numéro de caractère affectera le résultat, car s'il y a 2 caractères, le codage UTF-8 occupe 6 octets et le décodage dans GBK peut être décodé en 3 caractères. S'il s'agit de 1 caractère, il y aura un octet non apparable, qui est l'endroit où le point d'interrogation sur la figure.
Pour être plus précis, le codage UTF-8 du mot "Chine" dans le fichier source est e4b8ade59bbd. Le compilateur le décode dans GBK. Les paires de 3 octets recherchent CP936 pour obtenir 3 valeurs Unicode, qui sont respectivement 6D93E15E6D57, correspondant aux trois caractères étranges du graphique de résultat. Comme le montre la figure ci-dessous, après compilation, ces trois Unicodes sont en fait stockés dans un codage de type UTF-8 dans le fichier .class. Lors de l'exécution, l'Unicode est stocké dans le JVM. Cependant, lorsque la sortie finale est sortie, elle sera toujours codée et transmise au terminal. Le codage convenu cette fois est le codage ensemble par la zone du système, donc si les paramètres de codage terminal sont modifiés, il sera toujours brouillé. Notre E15E ici ne définit pas les caractères correspondants dans la norme Unicode, donc l'affichage sera différent sous différentes polices sur différentes plates-formes.
On peut imaginer que si le fichier source est stocké dans le codage de GBK, puis trompe le compilateur pour dire que c'est UTF-8, il ne peut pas être compilé et transmis, peu importe le nombre de caractères chinois entrés, car le codage de l'UTF-8 est très régulière, et les octets combinés au hasard ne se contenteront pas des règles de codage UTF-8.
Bien sûr, le moyen le plus direct de permettre au compilateur de convertir correctement le codage en Unicode est de dire honnêtement au compilateur quel est le codage du fichier source.
4. Résumé
Après cette collection et cette expérience, j'ai appris de nombreux concepts liés au codage et je me suis familiarisé avec le processus spécifique de conversion de codage. Ces idées peuvent être généralisées à divers langages de programmation, et les principes de mise en œuvre sont similaires. Je pense donc que je n'ignorerai plus ce genre de problème à l'avenir.
Ce qui précède est tout le contenu de cet article sur les exemples de concept de codage tels que ANSI, Unicode, BMP, UTF, etc. J'espère que cela sera utile à tout le monde. Les amis intéressés peuvent continuer à se référer à d'autres sujets connexes sur ce site. S'il y a des lacunes, veuillez laisser un message pour le signaler. Merci vos amis pour votre soutien pour ce site!