[PHP] unicode码、UTF-8码、字符的相互转换

unicode与UTF-8

unicode是一个大型字符集,unicode字符集收录了世界上所有的字符,并且为每一个字符分配了一个唯一的数字类型的ID值,这个ID在英文中叫unicode code point,下文统称为unicode码。例如中文的 “” 字,它的unicode码是\x4e25(十六进制)。因此unicode本质上是一个key-value映射表,key是unicode码,value是对应的字符。

下文中的十六进制数字,统一会在数字的前面加上 \x 前缀,方便识别。

接下来的问题是,这个unicode码在计算机中怎么存储?这就需要用到编码方案。

在UTF-32(UCS-4)编码方案中,每个unicode码均使用4个字节来存储,因此它是一种定长的编码方案。但是这种方案有一个很大的缺点,就是浪费存储空间。例如英文字母a,它的unicode码是\x61,转换为二进制是01100001,只需要一个字节就可以存储,但使用UTF-32编码的话,就需要用4个字节,有3个字节是多余的。因此,这种编码很少有人使用。

为了解决存储空间的问题,UTF-8就产生了,它是一种不定长的编码方案,每一个unicode码使用1~4字节的空间来存储,例如英文字母a,使用1个字节存储,中文汉字“”,使用3个字节存储。相比UTF-32编码的4字节,节省了不少存储空间。具体unicode码和UTF-8码之间如何转换,可以参考这篇文章

UTF-8与UTF-32的另外一个区别
例如汉字“严”,它的unicode码是\x4e25,这个unicode码在UTF-32中的编码值也是\x4e25,编码值使用4字节存储。而在UTF-8中,它的编码值是\xe4b8a5,使用3字节存储。也就是,在UTF-8中,除了单字节的ASCII字符以外,其它字符的编码值与其对应的unicode码是不一样的。

使用PHP互相转换unicode码、UTF-8码和字符

代码如下:

<?php

class Unicode
{

    private function checkSingleChar(string $char)
    {
        if (mb_strlen($char) !== 1) {
            throw new Exception("不是单个字符: $char");
        }
    }

    private function checkHexString(string $hex)
    {
        if (!ctype_xdigit($hex)) {
            throw new Exception("不是十六进制字符串: $hex");
        }
    }

    /**
     * 查询单个字符的unicode码(unicode code point)
     *
     * @param string $char 字符
     *
     * @return string 十六进制表示的unicode码
     * @throws Exception
     */
    public function charToUnicode(string $char): string
    {
        $this->checkSingleChar($char);
        return dechex(mb_ord($char));
    }

    /**
     * 查询单个字符的UTF-8编码值
     *
     * @param string $char 字符
     *
     * @return string 十六进制表示的UTF-8编码值
     * @throws Exception
     */
    public function charToUtf8(string $char): string
    {
        $this->checkSingleChar($char);
        return bin2hex($char);
    }

    /**
     * 将unicode码转换为字符
     *
     * @param string $hex 十六进制表示的unicode码
     *
     * @return string 字符
     * @throws Exception
     */
    public function unicodeToChar(string $hex): string
    {
        $this->checkHexString($hex);
        $char = mb_chr(hexdec($hex));
        if ($char === false) {
            throw new Exception('转换失败');
        }
        return $char;
    }

    /**
     * 将unicode码转换为UTF-8码
     *
     * @param string $hex 十六进制表示的unicode码
     *
     * @return string 十六进制表示的UTF-8编码值
     * @throws Exception
     */
    public function unicodeToUtf8(string $hex): string
    {
        return $this->charToUtf8($this->unicodeToChar($hex));
    }

    /**
     * 将UTF-8码转换为字符
     *
     * @param string $hex 十六进制表示的UTF-8编码值
     *
     * @return string
     * @throws Exception
     */
    public function utf8ToChar(string $hex): string
    {
        $this->checkHexString($hex);
        $char = pack('H*', $hex);
        if ($char === false) {
            throw new Exception('转换失败');
        }
        $this->checkSingleChar($char);
        return $char;
    }

    /**
     * 将UTF-8码转换为unicode码
     *
     * @param string $hex 十六进制表示的UTF-8编码值
     *
     * @return string 十六进制表示的unicode码
     * @throws Exception
     */
    public function utf8ToUnicode(string $hex): string
    {
        return $this->charToUnicode($this->utf8ToChar($hex));
    }

}

使用示例:

$unicode = new Unicode();

$char = '万';
$utf8HexCode = $unicode->charToUtf8($char);
$unicodeHexCode = $unicode->charToUnicode($char);

printf("字符[%s]的unicode码是:%s\n", $char, $unicodeHexCode);
printf("字符[%s]的UTF-8码是:%s\n", $char, $utf8HexCode);
printf("unicode码[%s]对应的字符是:%s\n", $unicodeHexCode, $unicode->unicodeToChar($unicodeHexCode));
printf("UTF-8码[%s]对应的字符是:%s\n", $utf8HexCode, $unicode->utf8ToChar($utf8HexCode));
printf("unicode码[%s]对应的UTF-8码是:%s\n", $unicodeHexCode, $unicode->unicodeToUtf8($unicodeHexCode));
printf("UTF-8码[%s]对应的unicode码是:%s\n", $utf8HexCode, $unicode->utf8ToUnicode($utf8HexCode));

以上示例如果正确运行,会输出:

字符[万]的unicode码是:4e07
字符[万]的UTF-8码是:e4b887
unicode码[4e07]对应的字符是:万
UTF-8码[e4b887]对应的字符是:万
unicode码[4e07]对应的UTF-8码是:e4b887
UTF-8码[e4b887]对应的unicode码是:4e07

参考文献

  1. unicode码与UTF-8码映射表
  2. 细说:Unicode, UTF-8, UTF-16, UTF-32, UCS-2, UCS-4
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值