前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >那些坑人的乱码问题(中)

那些坑人的乱码问题(中)

作者头像
一个无聊的人
发布2022-08-26 09:51:57
发布2022-08-26 09:51:57
6770
举报

Unicode编码

上篇中已经讲了编码的基本概念,本来是直接写MySQL的编码问题的,但是觉的Unicode字符集和UTF编码太重要了,以至于不能忽略这部分内容。

Unicode出现原因这里不再赘述,再次强调对于 Unicode的一些误解,它仅仅只是一个字符集,不是编码,规定了符号对应的二进制代码,至于这个二进制代码如何存储则没有任何规定。它的目标就是为每个字符规定一个用来表示该字符的数字,仅此而已。Unicode编码是统一标准没错,但并不是收录了世界上已有的所有字符,有部分字符是没有收录的,也就是Unicode编码并没有完全兼容所有的字符编码系统

Unicode的编码范围为U+0000 ~U+10FFFF,不难计算出一共包含16^4∗17=1114112个码位。

整个编码空间划分为0~16共17个平面(plane简单理解划分了17块),每个plane包含16^4=65536个码位,其中0号空间叫做基本多文种平面,其他平面叫做补充平面。

plane

编码范围

名称

0

U+0000 ~ U+FFFF

基本多文种平面

1

U+10000 ~ U+1FFFF

多文种补充平面

2

U+20000 ~ U+2FFFF

表意文字补充平面

3

U+30000 ~ U+3FFFF

表意文字第三平面

4~13

U+40000 ~ U+4FFFF

未使用

14

U+E0000 ~ U+EFFFF

特别用途补充平面

15~16

U+F0000 ~ U+10FFFF

保留作为私人使用区

基本多文种平面

即第0平面,这个平面收录了几乎所有现代语言的常用字符和符号,也是最常使用的一个平面,这个平面除了常规的字符以外,还有两部分比较特殊:

代理:在第0平面中U+D800 ~ U+DFFF2048个码位被保留作为代理:

高代理:U+D800 ~ U+DBFF 低代理:U+DC00 ~ U+DFFF 这里他们的作用不做讲解,放在后文讲UTF-16时讲解。

变量选择器:U+FE00 ~ U+FE0F

变量选择器是用来指明特定字符的选择类型,这几个特定字符其实我们每个人都是使用,就是我们平时聊天软件中使用的emoji表情,比如😊😢🌹,下面讲几个比较有意思的:

1)Emoji_presentation_sequence:

VS15(U+FE0E)标明该文字是文字,VS16(U+FE0F)标明该文字是Emoji。Unicode 标准定义了一个包含1182 个表情的基本 emoji 符号集, emoji 字符可能是以 text 风格呈现,或者以 emoji 风格呈现,一般emoji 风格呈现的字符是彩色的,而 text 风格渲染的字符是黑白的,具体两种风格呈现出来是什么样子往往与具体的平台有关。我们可以通过修饰符(U+FE0F 与 U+FE0F)指定字符以 text 或 emoji 风格字符。下面是一个例子:

2)ZWJ sequence:

零宽度连接符U+200D连接多个码位,我们可以将多个表情使用这个连接符连接生成一个新的表情,这个其实我们经常使用,但是没注意而已,比如下图中的第二例第一个实际上是 👩❤️👩三个表情中间使用U+200D组合而成:

3)Modfier_Base_Sequence:

Modifier 是用于修饰的 unicode 字符,目前定义了U+1F3FB~U+1F3FF共五种修饰字符,分别表示颜色的由深及浅。显然不是所有的 emoji 字符都能被修饰,只有Modifier_Base 才是能被修饰的 emoji 字符,还记得iPhone各种颜色的小人吗,就是同一个emoji被不同的Modifier修饰,如下图所示:

4)Flag Sequence:

这类 emoji 串是通过两个地域指示符(regional_indicator) 组合的方式来表示一个国家的国旗,共有 26 个地域指示符,每个指示符又对应于一个英文字母含义,合法的 Flag Sequence 有 256 种(并不是所有的组合都合法,可以数一下自己的输入法表情候选框中有多少个国旗)。例如 U+1F1E8 为地域指示符 C,U+1F1F3 为地域指示符 N。U+1F1E8 U+1F1F3 组合为CN,指的是中国国旗。

5)KeyCap Sequence:

这类 emoji 字符是将数字、* 、 # 通过一个 U+20E3 字符转换为键帽的样式,由于这种样式要求必须以 emoji 风格展示,所以会在序列中添加样式限制 U+FE0F。例如 U+0023(#) U+FE0F(表示emoji风格显示) U+20E3 (键帽模式)的 emoji 样式即是 #️⃣。

补充平面

第1平面:多文种补充平面。包含古文字、专用文字、符号和特定领域用的标记,古文字诸如埃及象形文字,楔形文字等,现代音乐标记,上文讲的Emoji表情等都属于这个平面的范畴

第2平面:表意文字补充平面。主要对CJK的字符进行补充(CJK中日韩统一表意文字,目的是要把多国中意义相同、形状一样或稍异的表意文字在Unicode标准内赋予相同编码,但是中、日、韩文字里面相同的字用的是同一个Unicode字符很有争议,因为虽然是同一个字,但是在不同的语言里面应该是不同的字体,但是Unicode这样的规定就导致了无法给这个字符加上多种字体了)。

第3~13平面:暂时还没有分配任何字符。

第14平面:特别用途平面,240个(VS17~VS256)补充变量选择器补充就在这个平面定义。

第15、16平面:私用。

UTF-8

UTF-8上文已经介绍了,这里不再重复介绍,只是有个坑需要讲一下:我们知道UTF-8是对Unicode字符集的实现,按照道理是可以显示emoji表情的,但是发现只要把emoji存入数据库就会乱码,这是因为MySQL中的utf8编码并不是标准的UTF-8编码,utf8mb4才是标准的UTF-8编码,而MySQL中的utf8中的编码仅仅实现了unicode字符集的第一平面,上文提到emoji是在第二平面,所以会显示为乱码。MySQL在5.5.3之后增加了这个utf8mb4的编码,mb4就是most bytes 4的意思,专门用来兼容四字节的Unicode,他们区别如下:

1)都是unicode 字符集的实现,但utf8仅支持第一平面,一个字符最多使用3个字节存储,utf8mb4 一个字符最多使用4个字节存储。

2)对于第0平面字符,utf8和utf8mb4具有相同的编码,相同的长度;对于非第0平面字符,utf8mb4使用4个字节来存储,utf8不能存储非第0平面字符。

3)innodb中默认最大可对767个字节建立索引,因此使用utf8 的列最多可对767/3=255个字符建立索引,使用utf8mb4 的列最多可对767/4=191个字符建立索引。

UTF-16

在上文的介绍中提到了 Unicode平面的概念,我们知道Unicode是一个通用字符集,目标是将世界上所有的字符定义在一个集合里,但这是一个漫长的过程,不可能一次性定义完,而是分区定义的,每个区可以存放 65536 个(2^16)字符,这里的分区就是上文提到的平面(plane),这也就是平面的来源。

UTF-16结合了定长(UTF-32)和变长(UTF-8)两种编码方法的特点。它的编码规则很简单:基本平面的字符占用 2 个字节,辅助平面的字符占用 4 个字节。即UTF-16 的编码长度要么是 2 个字节(U+0000 到 U+FFFF)要么是 4 个字节(U+010000 到 U+10FFFF)。那么当我们遇到两个字节时如何知道是把这两个字节当作一个字符还是与后面的两个字节一起当作一个字符呢?

上文中提到在基本平面内U+D800 ~ U+DFFF共2048个码位被保留作为代理,这2048个码点没有对应任何字符,因此用来映射辅助平面的字符。

辅助平面的字符位共有 2^20 个,因此表示这些字符至少需要 20 个二进制位。UTF-16 将这 20 个二进制位分成两半,前 10 位映射在 U+D800 到 U+DBFF,称为高位(H),后 10 位映射在 U+DC00 到 U+DFFF,称为低位(L)。这样就可以将一个辅助平面的字符,被拆成两个基本平面的字符表示。因此当我们遇到两个字节,发现它的码点在 U+D800 到 U+DBFF 之间,就可以断定紧跟在后面的两个字节的码点,应该在 U+DC00 到 U+DFFF 之间,这四个字节必须放在一起解读

例如:汉字”𠮷”的 Unicode 码点为 0x20BB7,该码点不在基本平面的范围(0x0000 - 0xFFFF),因此需要使用四个字节表示。

1)首先用 0x20BB7 - 0x10000 = 0x10BB7为超出的部分,然后将其用 20 个二进制位表示(不足前面补 0 ),结果为00010000101110110111;

2)将前 10 位映射到 U+D800 到 U+DBFF 之间,后 10 位映射到 U+DC00 到 U+DFFF 即可。U+D800 对应的二进制数为 1101100000000000,直接填充后面的 10 个二进制位即可,得到 1101100001000010,转成 16 进制数则为 0xD842。同理可得,低位为 0xDFB7。

因此得出汉字”𠮷”的 UTF-16 编码为 0xD8420xDFB7。

UTF-32:

最简单的编码方式,每个字符占四个字节,缺点是浪费存储空间,这里不再详细介绍。

到此为止基础知识讲完了,下篇讲的MySQL乱码问题。

参考:https://segmentfault.com/a/1190000007594620 附录:https://apps.timwhitlock.info/emoji/tables/unicode#block-6c-other-additional-symbols

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Unicode编码
    • 基本多文种平面
      • 代理:在第0平面中U+D800 ~ U+DFFF2048个码位被保留作为代理:
      • 变量选择器:U+FE00 ~ U+FE0F
    • 补充平面
    • UTF-8
    • UTF-16
    • UTF-32:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档