神码问题 by 非主流童话

和各种合作提交接口打交道久了,基本都会遇到这种情况。提交到对方的数据出现乱码,达不到搜索目的,像这样:比如我们在浏览器下搜索“杯具”,提交到接口方很可能就会这样。

果然很杯具。

为什么会这样呢?归根结底是提交方的编码与解析方的字符编码不一致。这一类的令人头疼的、破费周折的问题,我们管它叫神码问题

下面, 我们来详细地说一说这个神码。

显然,只有发送端和接收端指代的是同样的内容,我们才能得到正确的结果。那么,对于一次搜索请求中关键字的提交,我们有3种方式,简称发1,发2,发3:

(发1)表单POST提交

(发2)表单GET提交

(发3)将搜索项直接内含在url里,如http://zh.wikipedia.org/wiki/春节 。

先来讨论发1和发2情况。

在阮一峰的这篇文章中,我们看到,对于GET和POST,在页面进行提交的时候,提交过去的编码,就是提交页面的字符编码。所不同的是,如果采用的是GET方式,对于中文,浏览器还会依照当前字符编码做URL编码,那么如果是GET提交过去的,接收方还要按照约定先做一次URL解码。

这样看,如果提交方、接收方是同一字符编码,则不会有神码问题。

然而事情不会是这么简单,单就简体中文世界来讲,就有GB2312 , GBK , GB18030 , UTF-8几种常见的字符编码。对于GB系之间,除了使用特别罕用的考验人品的字符之外,其他一般可以认为是一种编码。但是GB系和UTF-8之间互相提交常常会出现问题。比如我们前文提到的就是UTF-8页面向GBK页面提交的情形。当编码到达接收方,接收方执拗地用自己的字符编码来解释另一种字符编码,于是神码出没。

好吧,那问题从宏观上有了两种解决思路,简称解1,解2:

(解1) 发送方把自己的提交的字符编码变成接收方的字符编码

对于GB系向UTF-8提交,这个方法是可行的。JS的encodeURIComponent方法,会强制把提交参数换成UTF-8. 参见拔赤的这篇文章

对于UTF-8向GB系提交,这个方法有些复杂。网上有一些偏方,各有缺陷,所列如下:

解决方案 缺陷
设置form的accept-charset,置为GB系 IE系列不支持,甚至IE9
针对IE设置form的document.charset 页面不能多次提交,从第二次开始乱码
加载一个对应列表,提交前使用hash转码 列表体积可能会是一个问题,不过对于内部系统,或者要求速度不苛刻的站点可以使用。
针对IE,使用VBS,进行转码 IEonly,且不是每个字符都能无误地转码
引入后端proxy先提交到发送端的域,通过后端程序转码,之后提交给接收端 小题大做,影响效率。

好吧,对于IE,我们又一次给跪了。

综上,对于UTF-8向GB系提交,在发送端无解。

(解2) 发送方告诉接收方编码,接收方据此把提交过来的字符编码转化为自己需要的编码,然后完成搜索

这也是现在各大搜索引擎的标准做法。他们提供一个ie参数(input encoding), 表示输入的字符编码。

比如百度的字符编码是GB2312。如在网址导航个人版选择百度搜索,搜索雨伞,抓包会看到,http://www.baidu.com/s?wd=%E9%9B%A8%E4%BC%9E&ie=utf-8 这个地址。wd参数的字符编码就是UTF-8。因为我们提供了ie参数,百度会把这个字符编码先转换为GB2312,然后再进行搜索,然后用百度的字符编码显示出来。

无论是UTF-8向GB系提交还是GB系向UTF-8提交,甚至之后接收方需要转变字符编码,这个做法都属于屡试不爽型。这也是目前处理这类问题的首选做法。

综上,我们对于发1和发2两种情况,选择解二作为解决方案

现在再来说说发3的情况

这种URL很可能是接收端提供者,为了获取较易读的URL,在服务器端做的URL路由,但实际上 发3 所列的并不是标准的URL。上面提到的URL编码会要求把中文字符进行URL编码。因为标准中没有提到如何编码,所以浏览器在此,会有不同的处理方案。事实上,IE8及以上的浏览器,firefox,chrome等浏览器会把中文变成UTF-8编码进行处理。但是IE6/7 ,会使用系统默认编码作为发送方式。因此这样直接采用中文是不安全的, 接收端也会做大量的工作去普适各种编码。不过好在,我们有一个encodeURI利器,这样我们可以在发送时候将中文做一次编码强制转为UTF-8 , 接收端统一按UTF-8处理就可以了。

当然,还会有用户不老实的从浏览器自己输入中文地址,那么对于IE6/7会有问题。只是,一方面,这种行为不是正常的用户行为,另一方面使用IE6/7在当今看来是一种和在公众场合用扩音器播放《最炫民族风》并驾齐驱的非普通非文艺行为,因此可以果断鄙视之。

现在我们来总结下这篇文章的结论

1,对于get/post方式,发送方提供一个ie参数,用来表示提供的字符编码,接收方据此进行转码后,完成搜索和展示。

2,对于url直接带有中文(非GET参数)时,发送端在发送前用encodeURIComponent将查询串进行编码,接收方按UTF-8接收。

 

关于 “神码问题 by 非主流童话” 的 3 个意见

    1. 非主流童话 的 回复

      在浏览器地址栏直接输入的情况是本文讨论之外的情况,本文讨论的主要是通过表单提交的情况。
      您所说的问题实际上是两个不同的处理过程:
      1,浏览器直接输入
      GET中带中文的,这个根据url标准是不允许的。因此浏览器需要根据规则来决定对这些字符进行编码。这里面,各种浏览器处理有差异,目前IE系列是用gb2312作为编码传递过去的。chrome等会作为utf-8编码。
      您在IE浏览器下直接输入那个地址,中文实际是gb2312的编码,而告诉360so是utf-8的,实际编码和所声名给360so的不一致,这里可能会出现乱码。
      2,通过表单提交:
      这就是本文提到的“发2”的情况。浏览器会依照当前字符编码对不符合URL标准(中文)做URL编码。这时实际编码和声名给360so的编码是一致的,应该可以显示正常的搜索结果。

评论关闭。