这段时间在做的项目需要各种转码,需要抓取 GBK 编码的网页提取关键字然后返回 JSON 字符串,主要就是 GB 系和 Unicode 系的编码转换,这两系之间的编码转换还是比较顺利的,但是就是在生成 JSON 上面,为了让 JSON 中汉字更易读一些,把 \uXXX 类型的编码转换为汉字,使用原先自己博客的解决 json_encode 后中文的编码问题这篇文章,这篇文章中的方法之前也验证过,这次也是在本机验证通过,但是一传到服务器上面抓取的网页就有问题了。Google 了下,然后根据自己的情况,原来又是 Windows 和 Linux 之间的一个坑啊!

上面提到的文章中原文是

$str= preg_replace("#\\\u([0-9a-f]{4})#ie", "iconv('UCS-2', 'UTF-8', pack('H4', '\\1'))", $str);

这样的代码片段进行转换,可以看出 \uXXX 类型的编码是 UCS-2 的编码,需要找出来这样的四位 16 进制数,使用 pack 函数转换为真正的字符,然后再做转码,可是就是在本地测试好好的,然后转移到服务器上面就会出现问题。

问题就出在 iconv 转换上面,Linux 和 Windows 在 UCS-2 的编码上面有些不同,看过我以前的 关于字符编码 GB2312,UTF-8,GBK,BIG5 都是些什么这篇文章的读者可能记得小人国吃鸡蛋的故事,这次问题的核心就在这个鸡蛋的大头 (Big-Endian) 敲开还是从小头 (Little-Endian) 敲开,其中 Windows 和 Linux 都主张的是小尾序,而 Mac 主张的是大尾序。而 json_encode 生成汉字是大尾序的,所以转换编码后就会出现问题的。

解决办法就是指定 UCS-2 编码的具体情况,比如 UCS-2LE,对于这个情况那就把那个 iconv 函数写成

iconv('UCS-2BE', 'UTF-8', pack('H4', '\\1'))

这样给了 Linux 一个确定的值就能完成转换了。

还有最后一个疑问,就是 Windows 也是使用小尾序的编码为什么没有出现乱码,这就该 "BOM" 上场了,Windows 会自动根据 BOM 判断文字的大小尾序,所以会自动进行判断转换,Linux 需要人工进行指定。

扩展阅读