GO 输出浏览器能够识别的 Unicode
作者:
| 更新日期:特殊字符
本文首发于公众号:天空的代码世界,微信号:tiankonguse
一、背景
我负责一个 go 编程语言开发的 http 服务。
关于这个服务,之前已经写了两篇文章了,如下。
一段代码让服务性能降低100倍
go HTTP 服务如何同时支持 GET 与 POST
最近又有小伙伴反馈问题了,是个编码问题。
二、编码问题
有一天,有小伙伴反馈使用这个 http 接口,拉到数据不对。
我看了一眼截图,是一个 emoji 表情字符。
我心中想:不对呀,很早之前就做过处理,会转化为 unicode 编码的,不应该有问题呀。
浏览器打开接口一看,浏览器自动解析 JSON 后,确实变成特殊字符:O5
了。
而抓包看回包,确实是 unicode 字符 \u1f495
。
三、原因
我突然发现一点了差异点:其他编码都是4位的,如 \uxxxx
。
这个编码竟然是5位的,极有可能是这个导致的。
于是我开始大量的 google 去查询资料。
我搜索这个字符的官方文档时,文档中竟然也说 javascript 使用这个字符。
地址:https://unicodeplus.com/U+1F495
但是问题已经发生了,那显然,这个文档是有问题的。
于是我使用 \u1f495
这个关键词来搜索,查阅了无数资料,最终找到了原因。
地址:https://www.cyberdefinitions.com/symbols/heart-symbols/heart-couple.html
虽然搜索的页面都是纯英文的,但是这些简单的单词还是很容易阅读的。
图片中选择的英文可以看出,JavaScript 不识别 5 个字符的 unicode,需要转化为 UTF-16。
下面还附加了一个转化的文章地址。
地址:https://www.cyberdefinitions.com/symbols/converting-hexadecimal-to-UTF-16-format-for-JavaScript.html
四、Unicode 转化 UTF-16 原理
上面提到,查到的资料里给了一个 Unicode 转化 UTF-16 链接。
要理解这个链接里的文章,首先需要了解几个背景知识。
背景知识1:目前 unicode 的范围是 [0, 0x10FFFF]
,共 21 比特位。
背景知识2:一个 UTF-16 理论储存的范围是[0, 0xFFFF]
有了上面两个背景知识,我们就可以来看这个问题了。
如果一个 unicode 只使用一个 UTF-16 就可以储存下,那只需要使用一个即可。
如果储存不下,使用一个算法,两个 UTF-16 肯定是可以储存下所有的 unicode。
打开链接的文章后,截图如下
可以发现,算法有一堆魔数。
但是也不复杂,首先先减去 0x10000
。
背后的逻辑是,一个 utf-16 可以储存 0x10000
个 unicode,还剩余 0xFFFFF
个需要两个 utf-16 来储存。
既然明确需要两位了,减去偏移量也合理。
偏移量修正后,使用 0x400
进行对半分组,除法的结果为高位,取模的结果为低位。
高位储存 0X4FF
个数字,低位储存 0X4FF
个数字,交叉相乘刚好是 0xFFFFF
个数字。
0X4FF
只需要 10 比特位,剩余的几位使用魔数填充,就可以使用两位 UTF-16 来表达值比较大的 unicode 了。
不过看到这里,聪明的你肯定会有疑问:解析 UTF-16 时,怎么区分这个是一位的还是两位的呢?
万一两个一位的 UTF-16 某些位恰好与魔数相等,不就冲突了?
是的,为了解决这个问题,魔数的位置,不能有合法的 UTF-16 值才行。
于是,UTF-16 增加了一个补丁:[0xD800, 0xDFFF]
范围的值都是非法的,只能用于两位 UTF-16 使用。
这样,两个魔数都在这个非法值内,就可以避免冲突了。
五、GO 代码实现
这个算法很简单,本来我想自己实现的。
后来一想,GO 既然支持 utf8 库,会不会也支持 utf-16 库呢?
于是打开 go utf8 的源码,发现同目录就有个 utf16 的目录,里面就有这个算法的实现。
于是我通过调用 utf16.EncodeRune
函数就可以获取上面算法的结果了。
六、最后
通过这个问题,我还学会了 utf16 储存 unicode 的原理。
只是有个疑问:两位的 utf16 为何这样设计算法?
为何不能想 utf8 那样,通过最高位比特位是否为 1 来判断?
加油,打工人。
《完》
-EOF-
本文公众号:天空的代码世界
个人微信号:tiankonguse
公众号ID:tiankonguse-code
本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。