PHP 字符编码介绍及处理

#问题回顾


简历解析时收到一封邮件的附件,经过Ri检测时没有检测出来, 后经手动编码测试UTF-16LE格式的


在UTF-16文件的头有2个字节里做个标记: LE [0xFF, 0xFE], BE [0xFE, 0xFF],PHP对这种不标记不提供检测的机制,所有没有把该文件的编码格式检测出来


# 几个常用编码的介绍


一个对ASCII,Unicode,UTF-8等编码的简单介绍的文章,http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html


**ASCII**


    http://www.baike.com/wiki/ASCII%E7%BC%96%E7%A0%81


**Unicode**


    http://zh.wikipedia.org/wiki/Unicode


**UTF-8**


    http://zh.wikipedia.org/zh-cn/UTF-8


**GB2312 GBK GB18030**


    GB2312是对 ANSI 的简体中文扩展。GB2312的原型是一种区位码,这种编码把常见的汉字分区,每个汉字有对应的区号和位号,每个区有94个汉字或符号。


    例如:"我" 的区号是 46,位号是 50。使用GB2312的程序通常采用EUC储存方法,以便兼容于ASCII。浏览器编码表上的“GB2312”,通常都是指“EUC-CN”表示法(EUC全名为Extended Unix Code,是一个使用8位编码来表示字符的方法)。每个汉字及符号以两个字节来表示。第一个字节称为“高位字节”,第二个字节称为“低位字节”。例如“啊”字在大多数程序中,会以两个字节,0xB0(第一个字节)0xA1(第二个字节)储存。GB2312 因要与 ASCII 相兼容,所以每个字的区号和位号都加上 0xA0 得到两个最高位都是 "1" 的 8 位字节,这两个字节组合而成就是一个汉字的 GB2312 编码,GB2312 编码中小于 127 的字符与 ASCII 的相同。


    由于 GB2312 支持的汉字太少而且不支持繁体中文,所以 GBK 对 GB2312 进行了扩展,对低字节不再做大于 127 的规定,以支持繁体中文和更多的字符,GBK 共支持大概 22000 个字符,GB18030采用多字节编码,每个字可以由1个、2个或4个字节组成。在 GBK 的基础上又增加了藏文、蒙文、维吾尔文等主要的少数民族文字。


    详细可查看维基百科:
        http://zh.wikipedia.org/zh-cn/GB_2312
        http://zh.wikipedia.org/wiki/GBK
        http://zh.wikipedia.org/wiki/GB18030


# Little endian和Big endian


    以汉字”严“为例,Unicode码是4E25,需要用两个字节存储,一个字节是4E,另一个字节是25。存储的时候,4E在前,25在后,就是Big endian方式;25在前,4E在后,就是Little endian方式。


    Unicode规范中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做”零宽度非换行空格“(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且FF比FE大1。


    UTF-16编码格式的文本头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。


    http://en.wikipedia.org/wiki/Byte_order_mark


# 我们解析时常常会看到CP936,这是什么呢?


    CP所谓的代码页,代码页是字符集编码的别名,也有人称"内码表"。


    - 常见字符集与代码页直接映射是:


        CP    charset
        932 — 日文
        936 — 简体中文(GBK)
        949 — 韩文


# 在检测字符编码时,有时会返回EUC-CN,EUC又是什么呢?


    EUC全名为Extended Unix Code,是一个使用8位编码来表示字符的方法。EUC-CN是GB2312最常用的表示方法。


    举例来说,“啊”字是GB2312之中的第一个汉字,它的区位码是1601。在EUC-CN之中,它把0xA0+16=0xB0,0xA0+1=0xA1,得出0xB0A1。


# PHP编码处理


    1.编码识别:mb_detect_encoding函数可以检测字符串是什么编码,Ri解析中遇到的附件解析问题是因为PHP mb_detect_encoding 的一个bug,mb_detect_encoding does not seem to recognize UTF-16 encoded files properly(https://bugs.php.net/bug.php?id=45993),所以可以检验文件开头的字符,确定字符编码。
        function getFileEncoding ($string) {
            $encoding = mb_detect_encoding($string, array('ASCII', 'GB2312', 'GBK', 'UTF-8'));
            if (false === $encoding) {
                $first2 = substr($string, 0, 2);
                $first3 = substr($string, 0, 3);


                if ($first3 == chr(0x00) . chr(0x00) . chr(0xFE) . chr(0xFF)) {
                    return 'UTF-32BE';
                } else if ($first3 == chr(0xFF) . chr(0xFE) . chr(0x00) . chr(0x00)) {
                    return 'UTF-32LE';
                } else if ($first2 == chr(0xFE) . chr(0xFF)) {
                    return 'UTF-16BE';
                } else if ($first2 == chr(0xFF) . chr(0xFE)) {
                    return 'UTF-16LE';
                } else {
                    return false;
                }
            }
        }


    2.编码转换:我们系统中所用格式为UTF-8的格式,将其他格式转换为UTF-8,所以在转换过程中不会遇到什么问题,iconv有时遇到的问题,比如将字符串由UTF-8转换成GB2312的'—'的过程就会断掉,这些字符后面的文字就没有办法继续转换了,一种方法,iconv('utf-8', 'gb2312//IGNORE', 'www.111cn.net'),忽略不能转换的字符。第二种方法,扩大输出字符编码的范围,如iconv('utf-8', 'gbk', 'www.111cn.net')。


# iconv 原理说明


    iconv 字集转换系统只有三个函式,在很多系统中是宣告在 iconv.h 里头,使用上与一般在做档案读写的概念一样,先 '开启'、之后 '操作'、完毕后要 '关闭',这些函式包括:


    iconv_t iconv_open(const char *TOENC, const char *FROMENC)
    size_t iconv (iconv_t CD,
                  const char **INBUF,
                  size_t *INBYTESLEFT,
                  char **OUTBUF,
                  size_t *OUTBYTESLEFT
                 )
    int iconv_close(iconv_t CD)


    1.iconv_pen() 做 '开启' 动作,也就是当我们要将编码系统 A 转换到编码系统 B 时,必须先呼叫此函式,将 FROMENC 设成编码系统 A 的名字,同时>将 TOENC 设成编码系统 B 的名字,传回一个代表此转换管道的数据结构 iconv_t 供后续使用。


    2.iconv() 函式就是用来做实际编码系统转换工作的,若要做字符串 A 转换成字符串 B 时,字符串 A 是经由 *INBUF 传入,而 *INBYTESLEFT 则传入数组 A 的长度,一律以字节数来计算。而转换的结果则由 *OUTBUF 传回,同样的 *OUTBYTESLEFT 则为 *OUTBUF 的长度。如果转换成功了,则 *INBUF 最后会设在存放字符串 A 的数组末尾,而 *INBYTESLEFT 会设为目前此数组还剩多少字节可以用。而 *OUTBUF 与 *OUTBYTESLEFT 也是一样。因此我们可以在相同的 A、B >数组中重复呼叫此函式。举个例子来说,如果转换过程中在 A 数组里头遇到了不合法的字符而无法进行转换时,则 *INBUF 与 *OUTBUF 就会停在无法转换的位置上,并将已转换成功的结果传回,则我们可以自行决定看是要跳过那个不合法的字符,继续转换剩余的部分,或做其它的特殊处理 ...等等。


    3. iconv_close() 函式,就是当整个转换结束后,用来做「关闭档案」用的。

转载于:https://my.oschina.net/yueguanqun/blog/117669

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值