[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