编码方式和转码

以下内容是根据网上资料和个人理解整理出来的知识概要,如有错误,欢迎拍砖。

常用编码方式

常见的有四种:UTF-8、GB2312、GBK、GB18030。

先撇开UTF-8不谈,GB2312、GBK、GB18030这三种编码可以说都是为了汉字诞生的,三者的区别,可以简单理解为字符集大小的差异:GB2312 < GBK < GB18030。在这个不断扩充的过程中,少数民族语言、韩文、日文等各种字符也都被收录进来,发展到GB18030这一代,已经把世界大多民族的文字符号都囊括在内了。虽然GB18030的字符集可谓是最全面的,但是Windows系统下中文(中国)区域对应的默认代码页还是GBK。
那么,UTF-8又是怎么回事呢?要想理解她,必须先了解Unicode是什么东东。

大一统的Unicode

Unicode是为了世界和平和统一而诞生的。高大上吧,其实原理很简单:她把世界上所有的字符都列了出来,然后给每个字符分配了一个唯一编号,不管是什么平台也不管是什么语言,这个编号都不会发生改变。这种编码方式对应的通用字符集叫做UCS(Universal Character Set,UCS)-2。USC-2后面有个数字2是因为,目前的Unicode使用16位编码空间,即每个字符对应两个字节。
说到这里,终于可以揭开UTF-8的真面目了。她的全称是:Unicode Transformation Format-8bit。简单来说,UTF-8就是Unicode的一种实现方式或者说表现形式,主要是为了降低传输成本。她是一种可变长度字符编码方式,通常一个英文字符占用一个字节,一个汉字占用三个字节。对于绝大多数汉字而言,使用GB2312/GBK/GB18030是可以用两个字节表示的,因此对于汉字来说,使用UTF-8编码的弊端是会占用更多空间,但是由于UTF-8是基于Unicode的,她的优点在于:世界通用。

关于转码

铺垫了那么多,本文的重点终于出现了,那就是不同编码方式如何转换?其实很简单,那就是通过Unicode码做桥梁,实现GB2312/GBK/GB18030和UTF-8的相互转换。
可是如果这么简单,为何中文转码总会出现各种各样的问题呢?从这里开始水就深了,非要一句话总结的话,只能说汉字博大精深啊。下面列几个数字大家感受下。
在Unicode 5.0的99089个字符中,有71226个字符与汉字有关。它们的分布如下:

Block名称 开始码位 结束码位 字符数
CJK统一汉字 4E00 9FBB 20924  
CJK统一汉字扩充A 3400 4DB5 6582  
CJK统一汉字扩充B 20000 2A6D6 42711  
CJK兼容汉字 F900 FA2D 302  
CJK兼容汉字 FA30 FA6A 59  
CJK兼容汉字 FA70 FAD9 106
CJK兼容汉字补充 2F800 2FA1D 542
如果不算兼容汉字,Unicode目前支持的汉字总数是20924+6582+42711=70217。
更多相关资料请戳:
字符集和字符编码(Charset & Encoding)  
Unicode、GB2312、GBK和GB18030中的汉字  

GB18030编码研究以及GBK、GB18030与Unicode的映射 

case分享

下面说一下我遇到的转码问题。
一句话说重点:UTF-8转GBK时,映射到Unicode码中PUA(Private Use Area,用户造字区)码位的GBK码,用php中的mb_convert_encoding函数不能正确转码。
这句话看起来比较绕,简单点说是这样子的:
UTF-8转GBK需要先将UTF-8转Unicode,这一步是没有问题的。然后从Unicode转GBK,绝大部分汉字也都是没问题的。但是,,,有那么一些汉字(查资料得知是80个),GBK的收录时间早于Unicode,所以这些汉字的GBK码对应的Unicode码最开始是用Unicode自定义区的码位来映射的。大概是因为mb_convert_encoding函数只能支持映射到Unicode非PUA码位的字符转码,所以在给这些汉字转码时出现了错误。(这是推论出来的,未经验证)
解决这个问题有两个办法:
1、升级php版本到5.4+,因为mb_convert_encoding函数从5.4起支持GB18030字符集,这80个字符中有66个在GB18030中映射到了Unicode的非PUA码位。注意,对于其余14个字符,此办法仍旧不能正确转码。
2、针对这80个特殊字符,建立一张从Unicode码到GBK码的映射表。以下为测试通过的代码:

<?php
/**
 * 转码工具类
 */
class ds_TranscodingModel {
    
    /**
     * 特殊字符从unicode码到gbk码的映射
     */
    private $transDict = array(
        'e815' => 'fe50',
        'e816' => 'fe51',
        'e817' => 'fe52',
        'e818' => 'fe53',
        'e819' => 'fe54',
        'e81a' => 'fe55',
        'e81b' => 'fe56',
        'e81c' => 'fe57',
        'e81d' => 'fe58',
        'e81e' => 'fe59',
        'e81f' => 'fe5a',
        'e820' => 'fe5b',
        'e821' => 'fe5c',
        'e822' => 'fe5d',
        'e823' => 'fe5e',
        'e824' => 'fe5f',
        'e825' => 'fe60',
        'e826' => 'fe61',
        'e827' => 'fe62',
        'e828' => 'fe63',
        'e829' => 'fe64',
        'e82a' => 'fe65',
        'e82b' => 'fe66',
        'e82c' => 'fe67',
        'e82d' => 'fe68',
        'e82e' => 'fe69',
        'e82f' => 'fe6a',
        'e830' => 'fe6b',
        'e831' => 'fe6c',
        'e832' => 'fe6d',
        'e833' => 'fe6e',
        'e834' => 'fe6f',
        'e835' => 'fe70',
        'e836' => 'fe71',
        'e837' => 'fe72',
        'e838' => 'fe73',
        'e839' => 'fe74',
        'e83a' => 'fe75',
        'e83b' => 'fe76',
        'e83c' => 'fe77',
        'e83d' => 'fe78',
        'e83e' => 'fe79',
        'e83f' => 'fe7a',
        'e840' => 'fe7b',
        'e841' => 'fe7c',
        'e842' => 'fe7d',
        'e843' => 'fe7e',
        'e844' => 'fe80',
        'e845' => 'fe81',
        'e846' => 'fe82',
        'e847' => 'fe83',
        'e848' => 'fe84',
        'e849' => 'fe85',
        'e84a' => 'fe86',
        'e84b' => 'fe87',
        'e84c' => 'fe88',
        'e84d' => 'fe89',
        'e84e' => 'fe8a',
        'e84f' => 'fe8b',
        'e850' => 'fe8c',
        'e851' => 'fe8d',
        'e852' => 'fe8e',
        'e853' => 'fe8f',
        'e854' => 'fe90',
        'e855' => 'fe91',
        'e856' => 'fe92',
        'e857' => 'fe93',
        'e858' => 'fe94',
        'e859' => 'fe95',
        'e85a' => 'fe96',
        'e85b' => 'fe97',
        'e85c' => 'fe98',
        'e85d' => 'fe99',
        'e85e' => 'fe9a',
        'e85f' => 'fe9b',
        'e860' => 'fe9c',
        'e861' => 'fe9d',
        'e862' => 'fe9e',
        'e863' => 'fe9f',
        'e864' => 'fea0',
    );
    
    /**
     * 汉字转Unicode编码
     * @param string $str 原始汉字的字符串
     * @param string $encoding 原始汉字的编码
     * @param boot $ishex 是否为十六进制表示(支持十六进制和十进制)
     * @param string $prefix 编码后的前缀
     * @param string $postfix 编码后的后缀
     */
    function unicode_encode($str, $encoding = 'UTF-8', $ishex = false, $prefix = '&#', $postfix = ';') {
        $str = mb_convert_encoding($str, 'UCS-2', $encoding);
        $arrstr = str_split($str, 2);
        $unistr = '';
        for($i = 0, $len = count($arrstr); $i < $len; $i ++) {
            $dec = $ishex ? bin2hex($arrstr[$i]) : hexdec(bin2hex($arrstr[$i]));
            $unistr .= $prefix . $dec . $postfix;
        }
        return $unistr;
    }
    
    /**
     * Unicode编码转汉字
     * @param string $str Unicode编码的字符串
     * @param string $decoding 原始汉字的编码
     * @param boot $ishex 是否为十六进制表示(支持十六进制和十进制)
     * @param string $prefix 编码后的前缀
     * @param string $postfix 编码后的后缀
     */
    function unicode_decode($unistr, $encoding = 'UTF-8', $ishex = false, $prefix = '&#', $postfix = ';') {
        $arruni = explode($prefix, $unistr);
        $unistr = '';
        for($i = 1, $len = count($arruni); $i < $len; $i ++) {
            if (strlen($postfix) > 0) {
                $arruni[$i] = substr($arruni[$i], 0, strlen($arruni[$i]) - strlen($postfix));
            }
            $temp = $ishex ? hexdec($arruni[$i]) : intval($arruni[$i]);
            $unicode = ($temp < 256) ? chr(0) . chr($temp) : chr($temp / 256) . chr($temp % 256);
            $tempHex = bin2hex($unicode);
            if(array_key_exists($tempHex, $this->transDict)) {
                $unistr .= pack("H*",$this->transDict[$tempHex]);
            } else {
                $unistr .= mb_convert_encoding($unicode, $encoding, 'UCS-2');
            }
        }
        return $unistr;
    }
    
    /**
     * utf8编码转gbk编码
     * @param string $str            
     * @return string
     */
    function utf8_to_gbk($str) {
        $gbkStr = mb_convert_encoding($str, 'GBK', 'UTF-8');
        $utf8_str = mb_convert_encoding($gbkStr, 'UTF-8', 'GBK');
        if($utf8_str == $str) {
            return $gbkStr;
        }
        $unistr = self::unicode_encode($str, 'UTF-8');
        $str = self::unicode_decode($unistr, 'GBK');
        return $str;
    }

?>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值