源代码的字符集不同带来的问题

这两天在研究 Jobs Digg 的中文 URL 编码乱码的问题,我发现原来是由源代码字符集和数据输入输出字符集不同造成的。

这个乱码问题是这样的。在 Jobs Digg 的网站上,中文分类的链接里 URL 编码出错,无法还原成原来的中文名称。比如,UTF-8 的“招聘”这两个中文的 URL 编码应该是 %E6%8B%9B%E8%81%98,而出错的串变成 ae%8B%9Be%81%98。

产生这个问题的原因是在添加/更新分类的时候,Pligg 会把这个分类名称另外保存成一个 category_safe_name,代码中是先用 str_replace 函数把分类名称中可能包含的特殊字符转换成安全字符,再进行 URLEncode。

页面的输出是 UTF-8 字符集的,浏览器提交这个字段的时候也是用 UTF-8 编码的方式提交到服务器的,所以在 str_replace 的参数中,输入的字符字节是 e6 8b 9b e8 81 98 (假设提交的分类名称是“招聘”)。

处理分类名称的源文件用的是 latin1 字符集(单字节字符),在 str_replace 的特殊字符列表中,包含一些 latin 字符,比如 latin 字母中的修饰字符 è 在 latin1 字符集中的编码是 e8,æ 在 latin1 字符集中的编码是 e6。而中文 UTF-8 是多字节的,在多个字节中,就有和 latin 字符相同的字节。

pligg 的 admin/admin_categories.php 源文件中,处理提交的分类名称的函数中包含这样的代码:

function makeCategoryFriendly($input) {
    $input = utf8_substr($input, 0, 240);
    $output = utf8_strtolower($output);
    $output = trim($input);
    ......
    $output = str_replace("è", "e", $output);
    ......
    $output = str_replace("æ", "ae", $output);
    ......
    return urlencode($output);
}

所以“招聘”这两个字处理完之后变成 ae%8B%9Be%81%98 这样的串了。

我原先提了一个解决办法,用 mb_ereg_replace() 函数替代 str_replace() 函数,并用 mb_regex_encoding('UTF-8') 来设置多字节正则串的字符集为 UTF-8,然后重新更新分类名称,这样中文名称就不会被单字节的 latin 字符分解成乱码了。

但这个方法会有一个问题,由于源文件还是 latin1 编码,所以如果同一个 latin 字符从浏览器以 UTF-8 提交,与源文件里的 latin 字母编码不同而无法替换。

更好的方法是统一将源代码文件转换成 UTF-8 编码,并以 UTF-8 方式处理。

国内一些程序员遇到中文显示乱码的问题,也可能是不注意源文件的字符集造成的。一个项目中包含采用不同字符集的源文件,这样就有可能造成字符显示成乱码。

总结:

  1. 上面太罗嗦了 -___-
  2. str_replace 函数是以单字节方式处理的,中文字符串应该用多字节函数来处理
  3. 统一项目中用到的字符集,尽量采用 UTF-8 编码

相关文章

Tags: , , | Category: LAMP, Web

By belltoy On March 29, 2009 3:42 AM | delicious 收藏 | Twitter 分享

2 条评论

  1. kirin 说:

    咦,这个网站的服务器代码是你在管理了吗

  2. belltoy 说:

    不是的,因为 Pligg 是开源的,所以我直接去下源码来看了。

添加评论

(若看不到验证码,请点击此处。)