Unicode转中文也有坑?!看我踩过的那些坑

最近在搞一个国际化的项目,需要处理各种语言的字符转换。这不,又遇到了Unicode转中文的老问题。说实话,这东西说简单也简单,说坑也多。今天就来聊聊这个,顺便分享几个我踩过的坑。

先来个最简单的例子,假设你有个Unicode字符串:"\u4f60\u597d",想把它转成中文"你好"。用PHP简直不要太简单:

$unicodeStr = "\u4f60\u597d";

echo $unicodeStr; // 直接输出就是"你好"

看,PHP就是这么贴心,自动帮你转好了。但是!注意这个但是,事情往往没这么简单。

现实中的坑往往出现在JSON数据里。比如从API拿到这样一个JSON:

{"text":"\u4f60\u597d"}

你用json_decode解析后:

$data = json_decode('{"text":"\u4f60\u597d"}', true);

echo $data['text']; // 还是输出"你好"

看起来没问题?别急,坑来了。如果这个JSON里的Unicode是带斜杠的,比如:

{"text":"\\u4f60\\u597d"}

这时候解析出来就是字符串"\u4f60\u597d",而不是转换后的中文。这时候就需要手动处理了:

function unicodeToChinese($str) {

return preg_replace_callback('/\\\\u([0-9a-fA-F]{4})/', function ($match) {

return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UCS-2BE');

}, $str);

}

$str = "\\u4f60\\u597d";

echo unicodeToChinese($str); // 输出"你好"

这个函数我用了好多年,稳如老狗。原理就是把\u后面的4位十六进制数转成UCS-2编码,再转成UTF-8。

说到编码转换,不得不提mb_convert_encoding这个函数。有一次我遇到个奇葩问题,从数据库读出来的Unicode字符串死活转不成中文。调试了半天才发现,数据库里的字符串居然是"U+4F60U+597D"这种格式。当时我的表情大概是这样的:(╯°□°)╯︵ ┻━┻

最后解决办法是这样的:

function weirdUnicodeToChinese($str) {

return preg_replace_callback('/U\+([0-9a-fA-F]{4})/', function ($match) {

}

$str = "U+4F60U+597D";

echo weirdUnicodeToChinese($str); // 还是输出"你好"

还有一个常见场景是从HTML实体转中文。比如"你好"对应的HTML实体是"你好"。处理这个可以用html_entity_decode:

$htmlEntity = "你好";

echo html_entity_decode($htmlEntity, ENT_QUOTES, 'UTF-8'); // 输出"你好"

注意这里的ENT_QUOTES参数,它会转换双引号和单引号。如果你不确定编码,最好明确指定UTF-8,不然可能会得到一堆乱码。

说到乱码,我不得不提Windows命令行这个坑货。有次我写了个脚本处理Unicode,在IDE里运行好好的,一到命令行就乱码。后来发现是命令行默认编码问题,解决方案是在脚本开头加上:

if (PHP_OS == 'WINNT') {

exec('chcp 65001');

}

这个命令把Windows命令行切换到UTF-8编码。不过要注意,改了编码后,某些老旧程序可能会出问题。

再来说说性能问题。如果你要处理大量Unicode字符串,直接用正则替换可能会比较慢。这时候可以考虑先json_encode再json_decode:

$temp = json_decode('"' . str_replace('"', '\"', $str) . '"');

echo $temp; // 输出"你好"

这个方法利用了JSON解析器的Unicode转换功能,速度比正则替换快不少。不过要注意处理字符串中的引号,不然会报错。

最后分享一个真实案例。有次对接第三方API,返回的JSON里混合了Unicode和HTML实体,大概是这样的:

{"message":"Hello \\u4f60\\u597d, welcome to 你好 world"}

处理这种混合字符串,我写了这样的函数:

function mixedDecode($str) {

// 先处理Unicode

$str = preg_replace_callback('/\\\\u([0-9a-fA-F]{4})/', function ($match) {

// 再处理HTML实体

return html_entity_decode($str, ENT_QUOTES, 'UTF-8');

}

$str = "Hello \\u4f60\\u597d, welcome to 你好 world";

echo mixedDecode($str); // 输出"Hello 你好, welcome to 你好 world"

这个函数先处理Unicode,再处理HTML实体,顺序不能反,不然可能会出问题。

总结一下,Unicode转中文看似简单,实际应用中会遇到各种奇葩情况。关键是要理解背后的编码原理,遇到问题时知道该往哪个方向排查。记住,大多数编码问题都是因为没搞清楚输入到底是什么格式,以及输出需要什么格式。

最后的最后,如果你还在用PHP5...算了不说了,升级到PHP8,处理Unicode的性能和稳定性都好很多。毕竟现在都2023年了,别再用那些老古董了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值