设为首页 收藏本站
查看: 771|回复: 0

[经验分享] [转]PHP程序中的汉字编码探讨

[复制链接]

尚未签到

发表于 2017-4-1 10:34:58 | 显示全部楼层 |阅读模式
  
最近在做一个百度词典的采集,http://dict.baidu.com,发现了一个可以值得探讨的一个问题,汉字的编码问题。首先,我们输入一个汉字词语进行搜索,比如,我们,URL上的地址变成http://dict.baidu.com/s?wd=%CE%D2%C3%C7,前面的http://dict.baidu.com/s?wd=就不用管了,都知道是什么意思,我们关注一下后面“%CE%D2%C3%C7”这几个东西。显然是词语“我们”这个的汉字编码。我们知道PHP有个函数urlencode,可以把汉字转换为类似那样行事的。,得到结果为:%E6%88%91%E4%BB%AC。
显然,不是%CE%D2%C3%C7这种格式的。是不是进制的问题呢?还是汉字编码的问题(UTF-8或者GB2312)。我们做一下实验。
我们先进行转码:
1
2
3
4
5
6
7

<?php
    $str = '我们';
    //iconv('utf-8','cp936',$str);
    //echo urlencode($str);
    iconv('cp936','utf-8',$str);
    echo urlencode($str);
?>

发现输出结果都为:%E6%88%91%E4%BB%AC,因为我测试的页面为utf-8的页面。所以结果一样。当页面为GBK或者GB2312的时候结果为:%CE%D2%C3%C7,这样,就和百度上的那个编码一致了。现在我们主要讨论页面编码为UTF8的时候,怎么得到正确的编码。
汉字的编码究竟是如何做的。我们知道,国内大部分都是GBK编码的,我们知道,GBK编码中一个汉字由二个字符组成,获取汉字字符串的方法如下:
1
2
3
4
5
6
7
8
9
10
11

<?php
    $string = "我们"; 
    $length = strlen($string); 
    for($i=0;$i<$length;$i++){ 
        if(ord($string[$i])>127){ 
        $result[] =  ord($string[$i]).' '.ord($string[++$i]); 
    } 
    } 
    var_dump($result); 
    
?>

由于一个汉字为两个字符组成,通过ord()函数获取字符的ASCII值如果大于127时,就可以确定当前字符为一个汉字的前半部分,还需要获取汉字的后半部分。当然,这种判断的方法要结合具体的开发环境,如果存在ASCII值大于127的单个字符,这种方法判断显然就不正确。得到结果为一个数组:
GBK编码的页面结果:
1
2
3
4
5
6

array(2) {
  [0]=>
  string(7) "206 210"
  [1]=>
  string(7) "195 199"
}

UTF-8的页面结果:
1
2
3
4
5
6
7
8

array(3) {
  [0]=>
  string(7) "230 136"
  [1]=>
  string(7) "145 228"
  [2]=>
  string(7) "187 172"
}

GB2312的页面结果:
1
2
3
4
5
6

array(2) {
  [0]=>
  string(7) "206 210"
  [1]=>
  string(7) "195 199"
}

从以上结果可以看出,如果页面编码为国标编码的时候,一个汉字是由两个字节组成。而页面编码为UTF8的时候,汉字是由三个字节组成的。但是进制都是十进制的,而我们需要的是十六进制的。那么怎么把十进制的汉字编码转换为十六进制呢?
可以采用以下办法,php由几个内建函数可以直接转换进制,decbin(),十进制转换为二进制;dechex(),十进制转换为十六进制;decoct(), 十进制转换为八进制。
1
2
3
4
5
6
7

<?php 
foreach($result as $v){ 
    $dec = explode(" ",$v); 
    $strings[] =  dechex($dec[0])." ".dechex($dec[1]); 

var_dump($strings); 
?>

UTF8下得到结果为:
1
2
3
4
5
6
7
8

array(3) {
  [0]=>
  string(5) "e6 88"
  [1]=>
  string(5) "91 e4"
  [2]=>
  string(5) "bb ac"
}

看到没,成了十六进制了,同理,转换二进制或者八进制只需将dechex函数换成decbin或者decoct就可以了。
现在我们明白汉字编码和转换进制问题了。
接着我们的问题继续,在UTF8页面上实现转换UTF8汉字为十六进制的GBK汉字编码。
首先学习两个函数:strtoupper() 函数把字符串转换为大写;base_convert() 函数在任意进制之间转换数字;iconv() 函数,实现各种字符集间的转换。mb_detect_encoding() 函数,判断汉字编码。
以上是分步进行转换,先得到汉字编码的十进制编码,然后我们用进制转换函数得到我们想要的汉字编码。下面我们依然这样做。
因为我们的页面是UTF8的页面。所以,我们先得到汉字的十进制编码,当然是UTF8下的编码,然后转成UTF8的十六进制编码。然后使用iconv函数进行字符集转换就了。废话少说,看代码:
1
2
3
4
5
6
7
8
9
10
11
12

<?php
function convertStr($str) {
    $strlength = strlen($str);
    $cstr = '';
    for($i = 0; $i < $strlength; $i++) {
        $cstr .= "%".strtoupper(base_convert(ord($str{$i}), 10, 16));
    }
    return $cstr;
}
$contents = ($contentscharset = mb_detect_encoding($s, "ASCII, UTF-8, GB2312, GBK")) == "GB2312" ? $s : iconv($contentscharset, "CP936", $s);
$w = convertStr($contents);
?>







现在$w就是我们想要的十进制GBK下的汉字编码了了。
convertStr()函数,把汉字以16进制输出。首先判断汉字编码如果是UTF8则由UTF8转换成GBK的。然后执行convertStr()函数,完成进制转换。得到结果为:%CE%D2%C3%C7,这样,我们就得到了这个UTF8下的GBK汉字编码了。
 
<?php
//测试时文件的编码方式要是UTF8
$str='中文a字1符';
echo strlen($str).'<br>';//14
echo mb_strlen($str,'utf8').'<br>';//6
echo mb_strlen($str,'gbk').'<br>';//8
echo mb_strlen($str,'gb2312').'<br>';//10
/*
结果分析:在strlen计算时,对待一个UTF8的中文字符是3个长度,所以“中文a字1符”长度是3*4+2=14
在mb_strlen计算时,选定内码为UTF8,则会将一个中文字符当作长度1来计算,所以“中文a字1符”长度是6 
*/
//利用这两个函数则可以联合计算出一个中英文混排的串的占位是多少(一个中文字符的占位是2,英文字符是1)
echo (strlen($str) + mb_strlen($str,'UTF8')) / 2; 
//例如 “中文a字1符” 的strlen($str)值是14,mb_strlen($str)值是6,则可以计算出“中文a字1符”的占位是10. 
echo mb_internal_encoding();


PHP内置的字符串长度函数strlen无法正确处理中文字符串,它得 到的只是字符串所占的字节数。对于GB2312的中文编码,strlen得到的值是汉字个数的2倍,而对于UTF-8编码的中文,就是3倍的差异了(在 UTF-8编码下,一个汉字占3个字节)。
采用mb_strlen函数可以较好地解决这个问题。mb_strlen的用法和 strlen类似,只不过它有第二个可选参数用于指定字符编码。例如得到UTF-8的字符串$str长度,可以用 mb_strlen($str,'UTF-8')。如果省略第二个参数,则会使用PHP的内部编码。内部编码可以通过 mb_internal_encoding()函数得到。需要注意的是,mb_strlen并不是PHP核心函数,使用前需要确保在php.ini中加载 了php_mbstring.dll,即确保“extension=php_mbstring.dll”这一行存在并且没有被注释掉,否则会出现未定义函 数的问题。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-358550-1-1.html 上篇帖子: PHP jqGrid 表格数据更新帮助代码 下篇帖子: Learning PHP -数据的存储与检索
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表