落后就要挨打,爱拼才会赢。有限的生命也可以创造奇迹,只要每天都有进步!
有一种拼博叫坚持...

首页>>PHP>>PHP中GBK编码的5C问题

PHP中GBK编码的5C问题

最近在工作遇到一个PHP在编译GBK编码中的少数繁体字时会出现问题。过程再现:
工作需求:
要将一个存有10万以上的用户名的txt导入相应的数据表中,且要注意执行效率。当然,用户名是随意的,中英文,偏僻字等等都有可能出现。
我采用一次导入,分批写入的方法。每次写入1000条记录,分多批写入,经过测试,效率是达到了,14万用了16s(要求是不超过60s),但是在插入 钱程似錦 这个用户时却出错了,经过排查,原来是5C在作怪。

首先,从 錦 这个字说起。在PHP5.3,GBK编码中,这个字不能正常用引号赋值,如:
$a = ‘錦’; 或
$a = “錦”;
这样的都会报错:Parse error: parse error in…
如一定要上面那样用,可以用这样代替:
$specialChar = <<<HTML

HTML;            

但是,后面加个空格或者在中单就正常,如:
$a = ‘錦 ’;//正常
$a = ‘胡錦涛’; //正常
经过mysql_escape_string()转义后,发现:
当$a = ‘胡錦涛’; 输出为:胡錦\涛,加了一个\,这就是为什么用”錦”会报错,它把后面的”给\”掉了。这也是为什么插入数据库时会报错。
$sql = “insert into table (id, uname, addtime) values (1,’奥马巴’,’2011-11-1716:14:56′), (2,’钱程似錦\’,’2011-11-17 16:14:56′)”;//截了

为什么会多出一个\呢?经过研究,原来是PHP5.3对GBK字符集下一个bug.GBK字符集的编码范围如下:
分区             高位   低位

●GBK/1:GB2312非汉字符号          : A1~A9 || A1~FE
●GBK/2:GB2312汉字               : B0~F7 || A1~FE
●GBK/3:扩充汉字                 : 81~A0 || 40~FE
●GBK/4:扩充汉字                       : AA~FE || 40~A0
●GBK/5:扩充非汉字                      : A8~A9 || 40~A0

mysql_escape_string与addslashes函数类似(唯一的区别:mysql_escape_string总是将“ ‘”转换成“\ ‘”,而addslashes在magic_quotes_sybase=on时将“ ‘”转换成“ ‘ ‘”在magic_quotes_sybase=off时将“ ‘”转换成“\ ‘”),一共要转义四个字符:’、”、/、NULL。NULL是字符串,不会产生问题,单引号 ‘ 和双引号 ” 的ASCII码十六进制分别是27和22,不在GBK字符集的范围内,所以也不会产生问题。而 / 的ASCII码是5C,在GBK扩充集的低位范围内,同时addslashes函数在运行是后不会考虑字符集,这样BUG就产生了。以5C结尾的繁体中文字,例如“躙”(0xDC5C),在运行addslashes函数过滤的时候,5C会被替换成5C5C,也就是说0xDC5C会被替换成0xDC5C5C,实际输出就是“躙/”。
同理,转义符剥离函数stripslashes也会出现BUG,造成乱码。下面是网上有人写的一个处理这个问题的函数,经过测试,这种方法不但可以避免BUG产生,还可以避免注入漏洞的产生,因为此函数不会对不属于GBK的字符进行转移,因此0xbf27不会被转义为0xbf5c27。函数如下:
// 特殊字符转义函数
function gbk_addslashes($text) {
for ( ; ; ) {
$i = mb_strpos($text, chr(92), 0, "GBK");
if ($i === false) break;
$T = mb_substr($text, 0, $i, "GBK") . chr(92) . chr(92);
$text = substr($text, strlen($T) - 1);
$OK .= $T;
}
$text = $OK . $text;
$text = str_replace(chr(39), chr(92) . chr(39), $text);
$text = str_replace(chr(34), chr(92) . chr(34), $text);
return $text;
}
// 转义符剥离函数
function gbk_stripslashes($text) {
$text = str_replace(chr(92) . chr(34), chr(34), $text);
$text = str_replace(chr(92) . chr(39), chr(39), $text);
for ( ; ; ) {
$i = mb_strpos($text, chr(92) . chr(92), 0, "GBK");
if ($i === false) break;
$T = mb_substr($text, 0, $i, "GBK") . chr(92);
$text = substr($text, strlen($T) + 1);
$OK .= $T;
}
$text = $OK . $text;
return $text;
}

注:ASCII码十进制27是空格,34是”,39是’,92是\.更多请访问:http://www.asciitable.com

在实际使用中,如果系统开启了魔法函数,那么先要用stripslashes或者gbk_stripslashes对变量进行转义符剥离,然后再使用gbk_addslashes进行转义。再找到上面函数之前,自己也写了一个函数,虽然需要手工扩充偏僻字,但还是可以达到同样的功能,在此记录一下:
function dealSpecialGbk($str) {
//目前中遇到过这三个字会出现这种情况
$specialChar = <<<HTML
錦,運, 躙
HTML;
$escapeChar = mysql_escape_string(trim($specialChar));
$escapearrChar = explode(',', $escapeChar);
$arrChar = explode(',', trim($specialChar));
$pos = strpos_array($str, $arrChar);
$str = mysql_escape_string($str);
if($pos !== false) {
$str = str_replace($escapearrChar, $arrChar, $str);
}
return $str;
}

对于GBK编码,需要进行以上的额外处理,对执行效率会有一定的影响,所以建议使用UTF-8通用编码。

更多GBK字符集及编码知识,请访问:
http://www.cnblogs.com/chenwenbiao/archive/2011/09/16/2178372.html
http://www.lexun.cn/thread-3439185-1-1.html

 

« 上一篇: 下一篇:»

相关文章:



One Comment (我要评论)

发表评论

You must be logged in to post a comment.