Maison > Java > javaDidacticiel > le corps du texte

Analyser les problèmes d'encodage chinois dans Java Web

怪我咯
Libérer: 2017-04-10 10:32:53
original
1803 Les gens l'ont consulté

Contexte :

Les problèmes de codage ont toujours troublé les développeurs de programmes, en particulier en Java, car Java est un langage multiplateforme et il y a beaucoup de commutations entre le codage sur différentes plates-formes. Ensuite, nous présenterons les causes profondes des problèmes d'encodage Java ; les différences entre plusieurs formats d'encodage souvent rencontrés en Java ; les scénarios qui nécessitent souvent un encodage en Java ; comment contrôler le format d'encodage d'une requête HTTP ; comment éviter les problèmes d'encodage chinois, etc.

1. Plusieurs formats d'encodage courants

1.1 Pourquoi devrions-nous encoder

  • La plus petite unité de stockage d'informations dans un ordinateur est de 1 octet, soit 8 bits, donc la plage de caractères pouvant être représentée est de 0 à 255.

  • Il y a trop de symboles à représenter et ne peut pas être entièrement représenté en 1 octet.

1.2 Comment traduire

Les ordinateurs offrent une variété de méthodes de traduction, les plus courantes incluent ASCII, ISO-8859-1, GB2312, GBK, UTF-8, UTF-16, etc. Ceux-ci stipulent tous les règles de conversion. Selon ces règles, l'ordinateur peut représenter correctement nos personnages. Ces formats d'encodage sont présentés ci-dessous :

  • Code ASCII

    Il y en a 128 au total, représentés par les 7 bits inférieurs de 1 octet, 0 ~ 31 sont des caractères de contrôle tels que le saut de ligne, le retour chariot, la suppression, etc., 32 ~ 126 sont des caractères d'impression, qui peuvent être saisis via le clavier et affiché.

  • ISO-8859-1


  • 128 caractères ne suffisent évidemment pas, c'est pourquoi l'organisation ISO s'est étendue sur la base de l'ASCII. Ils vont de ISO-8859-1 à ISO-8859-15. La première couvre la plupart des caractères et est la plus largement utilisée. ISO-8859-1 est toujours un codage sur un seul octet, qui peut représenter un total de 256 caractères.

  • GB2312


  • Il s'agit d'un codage sur deux octets, et la plage de codage totale est A1 ~ F7, où A1 ~ A9 est la zone de symboles, contenant un total de 682 symboles ; B0 ~ F7 est la zone de caractères chinois, contenant 6763 caractères chinois ;

  • GBk


  • GBK est la "Spécification d'extension du code interne des caractères chinois", qui est une extension de GB2312. Sa plage de codage est 8140 ~ FEFE (en supprimant XX7F). Il y a un total de 23940 points de code, qui peuvent représenter 21003 caractères chinois. avec le codage GB2312 et n'aura pas de caractères tronqués.

  • UTF-16


  • Il définit spécifiquement la manière dont les caractères Unicode sont accessibles sur l'ordinateur. UTF-16 utilise deux octets pour représenter le format de conversion Unicode. Il utilise une méthode de représentation de longueur fixe, c'est-à-dire que quel que soit le caractère, il est représenté par deux octets. Deux octets font 16 bits, c'est donc ce qu'on appelle UTF-16. Il est très pratique de représenter des caractères. Deux octets ne représentent pas un seul caractère, ce qui simplifie grandement les opérations sur les chaînes.

  • UTF-8


  • Bien qu'il soit simple et pratique pour UTF-16 d'utiliser uniformément deux octets pour représenter un caractère, une grande partie des caractères peut être représentée par un octet. S'il est représenté par deux octets, l'espace de stockage sera doublé. la bande passante du réseau est limitée. Dans ce cas, le trafic de transmission réseau sera augmenté. UTF-8 utilise une technologie de longueur variable. Chaque zone de codage a des longueurs de caractères différentes. Différents types de caractères peuvent être composés de 1 à 6 octets.

    UTF-8 a les règles d'encodage suivantes :

    • S'il s'agit de 1 octet, le bit le plus élevé (8ème bit) est 0, ce qui signifie qu'il s'agit d'un caractère ASCII (00 ~ 7F)

    • S'il s'agit de 1 octet, en commençant avec 11, alors le nombre de 1 consécutifs indique le nombre d'octets de ce caractère

    • Si c'est 1 octet, commencer par 10 signifie que si ce n'est pas le cas le premier octet, vous devez attendre avec impatience d'obtenir le premier octet du caractère actuel


2. Scénarios nécessitant du codage en Java

​2.1 Encodages présents dans les opérations d'E/S

Comme indiqué ci-dessus : la classe Reader est la classe parent pour la lecture des caractères dans les E/S Java, et la classe InputStream est la classe parent pour la lecture des octets. La classe InputStreamReader est le pont qui associe les octets aux caractères. pendant le processus d'E/S. Il gère la conversion des octets lus en caractères et confie à StreamDecoder la mise en œuvre du décodage d'octets spécifiques en caractères. Pendant le processus de décodage de StreamDecoder, le format de codage Charset doit être spécifié par l'utilisateur. Il convient de noter que si vous ne spécifiez pas Charset, le caractère par défaut défini dans l'environnement local sera utilisé. Par exemple, le codage GBK sera utilisé dans l'environnement chinois.

Par exemple, le morceau de code suivant implémente la fonction de lecture et d'écriture de fichiers :

 String file = "c:/stream.txt"; 
 String charset = "UTF-8"; 
 // 写字符换转成字节流
 FileOutputStream outputStream = new FileOutputStream(file); 
 OutputStreamWriter writer = new OutputStreamWriter( 
 outputStream, charset); 
 try { 
    writer.write("这是要保存的中文字符"); 
 } finally { 
    writer.close(); 
 } 
 // 读取字节转换成字符
 FileInputStream inputStream = new FileInputStream(file); 
 InputStreamReader reader = new InputStreamReader( 
 inputStream, charset); 
 StringBuffer buffer = new StringBuffer(); 
 char[] buf = new char[64]; 
 int count = 0; 
 try { 
    while ((count = reader.read(buf)) != -1) { 
        buffer.append(buffer, 0, count); 
    } 
 } finally { 
    reader.close(); 
 }
Copier après la connexion

Lorsque notre application implique des opérations d'E/S, tant que nous prêtons attention à spécifier un jeu de caractères Charset de codage et de décodage unifié, il n'y aura généralement pas de problèmes de code tronqué.

2.2 Codage en opérations de mémoire

Effectuez une conversion de type de données de caractères en octets en mémoire.

1. La classe String fournit des méthodes pour convertir des chaînes en octets et prend également en charge les constructeurs pour convertir des octets en chaînes.

String s  = "字符串";
byte[] b = s.getBytes("UTF-8");
String n = new String(b, "UTF-8");
Copier après la connexion

2. Charset fournit l'encodage et le décodage, qui correspondent respectivement à l'encodage de char[] en byte[] et au décodage de byte[] en char[].

Charset charset = Charset.forName("UTF-8");
ByteBuffer byteBuffer = charset.encode(string);
CharBuffer charBuffer = charset.decode(byteBuffer);
Copier après la connexion

​...

3. Comment encoder et décoder en Java

Diagramme de classes de codage Java

Définissez d'abord la classe Charset en fonction du charsetName spécifié via Charset.forName(charsetName), puis créez un objet CharsetEncoder en fonction de Charset, puis appelez CharsetEncoder.encode pour encoder la chaîne. Différents types d'encodage correspondront à une classe. le processus est effectué dans ces classes. Voici le chronogramme du processus d'encodage String.getBytes(charsetName)

Diagramme de séquence de codage Java

Comme le montre la figure ci-dessus, la classe Charset est trouvée en fonction de charsetName, puis CharsetEncoder est généré en fonction de cet encodage de jeu de caractères. Cette classe est la classe parent de tous les encodages de caractères, elle définit comment. pour implémenter l'encodage dans ses sous-classes.Avec Après avoir obtenu l'objet CharsetEncoder, vous pouvez appeler la méthode encode pour implémenter l'encodage. Il s'agit de la méthode de codage String.getBytes, et d'autres méthodes telles que StreamEncoder sont similaires.

Il apparaît souvent que les caractères chinois se transforment en « ? », ce qui est probablement dû à une mauvaise utilisation du codage ISO-8859-1. Les caractères chinois perdront des informations après le codage ISO-8859-1. Nous appelons généralement cela un « trou noir », qui absorbera les caractères inconnus. Étant donné que le codage du jeu de caractères par défaut de la plupart des frameworks ou systèmes Java de base est ISO-8859-1, il est facile d'obtenir des caractères tronqués. Nous analyserons plus tard comment différentes formes de caractères tronqués apparaissent.

Comparaison de plusieurs formats d'encodage

Il peut gérer les quatre formats de codage suivants pour les caractères chinois. Les règles de codage de GB2312 et GBK sont similaires, mais GBK a une plage plus large et peut gérer tous les caractères chinois. Par conséquent, lors de la comparaison de GB2312 avec GBK, GBK doit être sélectionné. UTF-16 et UTF-8 traitent tous deux le codage Unicode, et leurs règles de codage ne sont pas les mêmes. Relativement parlant, le codage UTF-16 est le plus efficace, il est plus facile de convertir des caractères en octets et il est préférable d'effectuer une chaîne. opérations. Il convient à une utilisation entre le disque local et la mémoire et peut basculer rapidement entre les caractères et les octets. Par exemple, le codage de la mémoire Java utilise le codage UTF-16. Cependant, il ne convient pas à la transmission entre réseaux, car la transmission réseau peut facilement endommager le flux d'octets. Une fois le flux d'octets endommagé, il sera difficile à récupérer. En comparaison, l'UTF-8 est plus adapté à la transmission réseau et utilise un seul. Stockage de 1 octets pour les caractères ASCII. De plus, les dommages causés à un seul caractère n'affecteront pas les autres caractères suivants. L'efficacité de l'encodage se situe entre GBK et UTF-16. Par conséquent, UTF-8 équilibre l'efficacité de l'encodage et la sécurité de l'encodage et constitue un encodage chinois idéal. méthode.

4. Codecs impliqués dans Java Web

Pour le chinois, où il y a des E/S, le codage sera impliqué. Comme mentionné précédemment, les opérations d'E/S provoqueront le codage, et la plupart des codes tronqués causés par les E/S sont des E/S réseau, car désormais presque toutes les applications. impliquent des opérations réseau et les données transmises sur le réseau sont en octets, donc toutes les données doivent être sérialisées en octets. Les données à sérialiser en Java doivent hériter de l'interface Serialisable.

Comment calculer la taille réelle d'un morceau de texte ? J'ai rencontré un problème un jour : je voulais trouver un moyen de compresser la taille du cookie et de réduire la quantité de transmission sur le réseau. À ce moment-là, j'ai choisi différents algorithmes de compression et j'ai découvert cela. le nombre de caractères a été réduit après compression, mais cela n'a pas entraîné de réduction du nombre d'octets. La soi-disant compression convertit uniquement plusieurs caractères à un octet en un caractère multi-octet via le codage. Ce qui est réduit, c'est String.length(), mais pas le nombre final d'octets. Par exemple, les deux caractères « ab » sont convertis en un caractère étrange grâce à un certain codage. Bien que le nombre de caractères passe de deux à un, si UTF-8 est utilisé pour coder ce caractère étrange, il peut devenir trois après le codage ou. plus d'octets. Pour la même raison, par exemple, si le nombre entier 1234567 est stocké sous forme de caractère, il faudra 7 octets pour l'encoder en UTF-8, et il faudra 14 octets pour l'encoder en UTF-16, mais ce n'est que prend 4 pour le stocker sous forme d'octet int à stocker. Par conséquent, cela n’a aucun sens d’examiner la taille d’un morceau de texte et la longueur des caractères eux-mêmes. Même si les mêmes caractères sont stockés dans des encodages différents, la taille de stockage finale sera différente, vous devez donc examiner le type d’encodage. des caractères aux octets.

  我们能够看到的汉字都是以字符形式出现的,例如在 Java 中“淘宝”两个字符,它在计算机中的数值 10 进制是 28120 和 23453,16 进制是 6bd8 和 5d9d,也就是这两个字符是由这两个数字唯一表示的。Java 中一个 char 是 16 个 bit 相当于两个字节,所以两个汉字用 char 表示在内存中占用相当于四个字节的空间。

  这两个问题搞清楚后,我们看一下 Java Web 中那些地方可能会存在编码转换?

  用户从浏览器端发起一个 HTTP 请求,需要存在编码的地方是 URL、Cookie、Parameter。服务器端接受到 HTTP 请求后要解析 HTTP 协议,其中 URI、Cookie 和 POST 表单参数需要解码,服务器端可能还需要读取数据库中的数据,本地或网络中其它地方的文本文件,这些数据都可能存在编码问题,当 Servlet 处理完所有请求的数据后,需要将这些数据再编码通过 Socket 发送到用户请求的浏览器里,再经过浏览器解码成为文本。这些过程如下图所示:

  一次 HTTP 请求的编码示例

  4.1 URL 的编解码

  用户提交一个 URL,这个 URL 中可能存在中文,因此需要编码,如何对这个 URL 进行编码?根据什么规则来编码?有如何来解码?如下图一个 URL:

  上图中以 Tomcat 作为 Servlet Engine 为例,它们分别对应到下面这些配置文件中:

  Port 对应在 Tomcat 的 中配置,而 Context Path 在 中配置,Servlet Path 在 Web 应用的 web.xml 中的

<servlet-mapping> 
        <servlet-name>junshanExample</servlet-name> 
        <url-pattern>/servlets/servlet/*</url-pattern> 
 </servlet-mapping>
Copier après la connexion

   中配置,PathInfo 是我们请求的具体的 Servlet,QueryString 是要传递的参数,注意这里是在浏览器里直接输入 URL 所以是通过 Get 方法请求的,如果是 POST 方法请求的话,QueryString 将通过表单方式提交到服务器端。

  上图中 PathInfo 和 QueryString 出现了中文,当我们在浏览器中直接输入这个 URL 时,在浏览器端和服务端会如何编码和解析这个 URL 呢?为了验证浏览器是怎么编码 URL 的我选择的是360极速浏览器并通过 Postman 插件观察我们请求的 URL 的实际的内容,以下是 URL:

  HTTP://localhost:8080/examples/servlets/servlet/君山?author=君山

  君山的编码结果是:e5 90 9b e5 b1 b1,和《深入分析 Java Web 技术内幕》中的结果不一样,这是因为我使用的浏览器和插件和原作者是有区别的,那么这些浏览器之间的默认编码是不一样的,原文中的结果是:

  君山的编码结果分别是:e5 90 9b e5 b1 b1,be fd c9 bd,查阅上一届的编码可知,PathInfo 是 UTF-8 编码而 QueryString 是经过 GBK 编码,至于为什么会有“%”?查阅 URL 的编码规范 RFC3986 可知浏览器编码 URL 是将非 ASCII 字符按照某种编码格式编码成 16 进制数字然后将每个 16 进制表示的字节前加上“%”,所以最终的 URL 就成了上图的格式了。

  从上面测试结果可知浏览器对 PathInfo 和 QueryString 的编码是不一样的,不同浏览器对 PathInfo 也可能不一样,这就对服务器的解码造成很大的困难,下面我们以 Tomcat 为例看一下,Tomcat 接受到这个 URL 是如何解码的。

  解析请求的 URL 是在 org.apache.coyote.HTTP11.InternalInputBuffer 的 parseRequestLine 方法中,这个方法把传过来的 URL 的 byte[] 设置到 org.apache.coyote.Request 的相应的属性中。这里的 URL 仍然是 byte 格式,转成 char 是在 org.apache.catalina.connector.CoyoteAdapter 的 convertURI 方法中完成的:

protected void convertURI(MessageBytes uri, Request request) 
 throws Exception { 
        ByteChunk bc = uri.getByteChunk(); 
        int length = bc.getLength(); 
        CharChunk cc = uri.getCharChunk(); 
        cc.allocate(length, -1); 
        String enc = connector.getURIEncoding(); 
        if (enc != null) { 
            B2CConverter conv = request.getURIConverter(); 
            try { 
                if (conv == null) { 
                    conv = new B2CConverter(enc); 
                    request.setURIConverter(conv); 
                } 
            } catch (IOException e) {...} 
            if (conv != null) { 
                try { 
                    conv.convert(bc, cc, cc.getBuffer().length - 
 cc.getEnd()); 
                    uri.setChars(cc.getBuffer(), cc.getStart(), 
 cc.getLength()); 
                    return; 
                } catch (IOException e) {...} 
            } 
        } 
        // Default encoding: fast conversion 
        byte[] bbuf = bc.getBuffer(); 
        char[] cbuf = cc.getBuffer(); 
        int start = bc.getStart(); 
        for (int i = 0; i < length; i++) { 
            cbuf[i] = (char) (bbuf[i + start] & 0xff); 
        } 
        uri.setChars(cbuf, 0, length); 
 }
Copier après la connexion

  从上面的代码中可以知道对 URL 的 URI 部分进行解码的字符集是在 connector 的 中定义的,如果没有定义,那么将以默认编码 ISO-8859-1 解析。所以如果有中文 URL 时最好把 URIEncoding 设置成 UTF-8 编码。

Comment analyser QueryString ? La QueryString de la requête HTTP GET et les paramètres de formulaire de la requête HTTP POST sont enregistrés en tant que paramètres et les valeurs des paramètres sont obtenues via request.getParameter. Ils sont décodés lors du premier appel de la méthode request.getParameter. Lorsque la méthode request.getParameter est appelée, elle appellera la méthode parseParameters de org.apache.catalina.connector.Request. Cette méthode décodera les paramètres transmis par GET et POST, mais leurs jeux de caractères de décodage peuvent être différents. Le décodage du formulaire POST sera introduit plus tard. Où est défini le jeu de caractères de décodage de QueryString ? Il est transmis au serveur via l'en-tête HTTP et se trouve également dans l'URL. Est-ce le même que le jeu de caractères de décodage de l'URI ? D'après les navigateurs précédents utilisant des formats d'encodage différents pour PathInfo et QueryString, on peut deviner que les jeux de caractères décodés ne seront certainement pas cohérents. En effet, le jeu de caractères de décodage de QueryString est soit le Charset défini dans ContentType dans l'en-tête, soit l'ISO-8859-1 par défaut. Pour utiliser l'encodage défini dans ContentType, vous devez définir le useBodyEncodingForURI est défini sur true. Le nom de cet élément de configuration est un peu déroutant. Il n'utilise pas BodyEncoding pour décoder l'intégralité de l'URI, mais utilise uniquement BodyEncoding pour décoder QueryString. Cela mérite une attention particulière.

À en juger par le processus d'encodage et de décodage d'URL ci-dessus, il est relativement compliqué et nous ne pouvons pas entièrement contrôler l'encodage et le décodage dans l'application. Par conséquent, nous devrions essayer d'éviter d'utiliser des caractères non-ASCII dans l'URL de notre application, sinon cela se produirait. sera très difficile. Vous pouvez rencontrer des caractères tronqués. Bien sûr, il est préférable de définir les deux paramètres URIEncoding et useBodyEncodingForURI dans

4.2 Encodage et décodage de l'en-tête HTTP

Lorsque le client initie une requête HTTP, en plus de l'URL ci-dessus, il peut également transmettre d'autres paramètres dans l'en-tête tels que Cookie, redirectPath, etc. Ces valeurs définies par l'utilisateur peuvent également avoir des problèmes d'encodage. Comment Tomcat les décode-t-il. ?

Le décodage des éléments de l'en-tête est également effectué en appelant request.getHeader. Si l'élément d'en-tête demandé n'est pas décodé, la méthode toString de MessageBytes est appelée. Le codage par défaut utilisé par cette méthode pour la conversion d'octet en caractère est également ISO-8859. -1. , et nous ne pouvons pas définir d'autres formats de décodage de l'en-tête, donc si vous définissez le décodage des caractères non-ASCII dans l'en-tête, il y aura certainement des caractères tronqués.

La même chose est vraie lorsque nous ajoutons un en-tête. Ne transmettez pas de caractères non-ASCII dans l'en-tête. Si nous devons les transmettre, nous pouvons d'abord encoder ces caractères avec org.apache.catalina.util.URLEncoder, puis les ajouter à l'en-tête. . De cette façon, les informations ne seront pas perdues lors du transfert du navigateur vers le serveur. Ce serait bien si nous les décodions en fonction du jeu de caractères correspondant lorsque nous voulons accéder à ces éléments.

4.3 Codage et décodage du formulaire POST

Comme mentionné précédemment, le décodage des paramètres soumis par le formulaire POST se produit lorsque request.getParameter est appelé pour la première fois. La méthode de transfert des paramètres du formulaire POST est différente de QueryString. Elle est transmise au serveur via le BODY de HTTP. Lorsque nous cliquons sur le bouton Soumettre sur la page, le navigateur encodera d'abord les paramètres remplis dans le formulaire selon le format d'encodage Charset de ContentType, puis les soumettra au serveur. Le serveur utilisera également le caractère défini dans ContentType pour le décodage. Par conséquent, les paramètres soumis via le formulaire POST ne posent généralement pas de problèmes, et ce codage de jeu de caractères est défini par nous-mêmes et peut être défini via request.setCharacterEncoding(charset).

De plus, pour les paramètres de type multipart/form-data, c'est-à-dire que le codage du fichier téléchargé utilise également le codage du jeu de caractères défini par ContentType. Il convient de noter que le fichier téléchargé est transmis au répertoire temporaire local du serveur dans un octet. stream.Ce processus n'implique pas de codage de caractères, mais le codage réel ajoute le contenu du fichier aux paramètres. S'il ne peut pas être codé en utilisant ce codage, le codage par défaut ISO-8859-1 sera utilisé.

4.4 Encodage et décodage HTTP BODY

Lorsque les ressources demandées par l'utilisateur ont été obtenues avec succès, le contenu sera renvoyé au navigateur client via Réponse. Ce processus doit d'abord être codé puis décodé par le navigateur. Le jeu de caractères d'encodage et de décodage de ce processus peut être défini via response.setCharacterEncoding, qui remplacera la valeur de request.getCharacterEncoding et reviendra au client via le Content-Type de l'en-tête. Lorsque le navigateur reçoit le flux de socket renvoyé, il. transmettra le jeu de caractères Content-Type à décoder. Si le Content-Type dans l'en-tête HTTP renvoyé ne définit pas le jeu de caractères, le navigateur le décodera en fonction du

  4.5 其它需要编码的地方

  除了 URL 和参数编码问题外,在服务端还有很多地方可能存在编码,如可能需要读取 xml、velocity 模版引擎、JSP 或者从数据库读取数据等。
xml 文件可以通过设置头来制定编码格式

<?xml version="1.0" encoding="UTF-8"?>
Copier après la connexion

  Velocity 模版设置编码格式:

services.VelocityService.input.encoding=UTF-8
Copier après la connexion

  JSP 设置编码格式:

 <%@page contentType="text/html; charset=UTF-8"%>
Copier après la connexion

  访问数据库都是通过客户端 JDBC 驱动来完成,用 JDBC 来存取数据要和数据的内置编码保持一致,可以通过设置 JDBC URL 来制定如 MySQL:url="jdbc:mysql://localhost:3306/DB?useUnicode=true&characterEncoding=GBK"。

  5、常见问题分析

  下面看一下,当我们碰到一些乱码时,应该怎么处理这些问题?出现乱码问题唯一的原因都是在 char 到 byte 或 byte 到 char 转换中编码和解码的字符集不一致导致的,由于往往一次操作涉及到多次编解码,所以出现乱码时很难查找到底是哪个环节出现了问题,下面就几种常见的现象进行分析。

  5.1 中文变成了看不懂的字符

  例如,字符串“淘!我喜欢!”变成了“Ì Ô £ ¡Î Ò Ï²»¶ £ ¡”编码过程如下图所示:

 

  字符串在解码时所用的字符集与编码字符集不一致导致汉字变成了看不懂的乱码,而且是一个汉字字符变成两个乱码字符。

  5.2 一个汉字变成一个问号

  例如,字符串“淘!我喜欢!”变成了“??????”编码过程如下图所示:

 

  将中文和中文符号经过不支持中文的 ISO-8859-1 编码后,所有字符变成了“?”,这是因为用 ISO-8859-1 进行编解码时遇到不在码值范围内的字符时统一用 3f 表示,这也就是通常所说的“黑洞”,所有 ISO-8859-1 不认识的字符都变成了“?”。

  5.3 一个汉字变成两个问号

  例如,字符串“淘!我喜欢!”变成了“????????????”编码过程如下图所示:

 

  这种情况比较复杂,中文经过多次编码,但是其中有一次编码或者解码不对仍然会出现中文字符变成“?”现象,出现这种情况要仔细查看中间的编码环节,找出出现编码错误的地方。

  5.4 一种不正常的正确编码

  还有一种情况是在我们通过 request.getParameter 获取参数值时,当我们直接调用

  String value = request.getParameter(name); 会出现乱码,但是如果用下面的方式

  String value = String(request.getParameter(name).getBytes(" ISO-8859-1"), "GBK");

  解析时取得的 value 会是正确的汉字字符,这种情况是怎么造成的呢?

  看下如所示:

 

  这种情况是这样的,ISO-8859-1 字符集的编码范围是 0000-00FF,正好和一个字节的编码范围相对应。这种特性保证了使用 ISO-8859-1 进行编码和解码可以保持编码数值“不变”。虽然中文字符在经过网络传输时,被错误地“拆”成了两个欧洲字符,但由于输出时也是用 ISO-8859-1,结果被“拆”开的中文字的两半又被合并在一起,从而又刚好组成了一个正确的汉字。虽然最终能取得正确的汉字,但是还是不建议用这种不正常的方式取得参数值,因为这中间增加了一次额外的编码与解码,这种情况出现乱码时因为 Tomcat 的配置文件中 useBodyEncodingForURI 配置项没有设置为”true”,从而造成第一次解析式用 ISO-8859-1 来解析才造成乱码的。

6. Résumé

Cet article résume d'abord les différences entre plusieurs formats d'encodage courants, puis présente plusieurs formats d'encodage prenant en charge le chinois et compare leurs scénarios d'utilisation. Ensuite, il présente les endroits de Java qui impliquent des problèmes de codage et la manière dont le codage est pris en charge en Java. En prenant comme exemple les E/S réseau, il se concentre sur les endroits où l'encodage existe dans les requêtes HTTP, ainsi que sur l'analyse du protocole HTTP par Tomcat, et enfin analyse les raisons des problèmes de code tronqué que nous rencontrons habituellement.

En résumé, pour résoudre le problème chinois, nous devons d'abord déterminer où se produira le codage caractère à octet et le décodage octet à caractère. Les endroits les plus courants sont la lecture et le stockage des données sur le disque, ou les données transitant par le réseau. . transmission. Ensuite, pour ces endroits, déterminez comment le framework ou le système qui exploite ces données contrôle l'encodage, définissez correctement le format d'encodage et évitez d'utiliser le format d'encodage par défaut du logiciel ou de la plate-forme du système d'exploitation.


Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!