对所有的乱码 Say Goodbye ——浅谈字符编码

· · 科技·工程

还在为乱码而焦头烂额吗?这里有解决所有乱码的方法。(前提是你的文件本身是有意义的文本且不是锟斤拷,cat /dev/urandom产生的乱码谁都没有办法)

常见的编码方式

见下表,将 Unicode 编号的二进制值从右往左、从低位到高位填入 x。首字节开头的 1 的数量即为这个字符占用的字节数,之后的字节会以 10 开头。常用汉字占 3 字节,一些生僻字会占 4 字节。Unicode 标准规定字符上限为 U+10FFFF,但是其实 UTF-8 还可以扩展。UTF-8 的码元(编码单位)为 1 字节。

::cute-table{tuack}

Unicode 编号 UTF-8 编码(二进制) 可编码位数
U+0 ~ U+7F 0xxxxxxx 7
U+80 ~ U+7FF 110xxxxx 10xxxxxx 11
U+800 ~ U+FFFF 1110xxxx 10xxxxxx 10xxxxxx 16
U+10000 ~ U+10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 21

::: :::info[UTF-16 编码规则]

对于 U+0 ~ U+FFFF 之间的字符,其 Unicode 编号即为一个码元;对于 U+10000 ~ U+10FFFF 的字符,把 Unicode 编号减去 0x10000 后的二进制值从右往左、从低位到高位填入 110110xx xxxxxxxx 110111xx xxxxxxxx。这两个码元直接对应的 U+D800 到 U+DFFF 区段被称为代理对,没有字符,所以不会和 2 字节编码的 UTF-16 码元混淆。

由于 UTF-16 码元为 2 字节,所以存在字节序问题。一般通过在文件开头包含一个 U+FEFF 字符的方式声明字节序吗,这个字符被称为 Byte Order Mark (BOM)。U+FFFE 不是字符。

::: :::info[UTF-32 编码规则]

直接将其 Unicode 编号作为一个 4 字节的码元。

:::

常见乱码及其解决方案

:::warning[注意]{open} 当你看见乱码的时候,不要保存,不要保存,不要保存!!! :::

:::info[锟斤拷] 这是由于 GBK 等其他编码的文本按 UTF-8 解码后保存,再按 GBK 解码的结果。根据 UTF-8 编码规则,不是所有字节序列都是合法的 UTF-8 编码。对于不符合规则的序列,UTF-8 用�(U+FFFD,REPLACEMENT CHARACTER,UTF-8:EF BF BD)替代,两个�按 GBK 解码之后就是一个锟斤拷。当看见锟斤拷的时候,你原来的内容已经永远丢失了。你的回忆不断改变着形态,坍缩成了无数的“锟斤拷”,至于该怎么办,你的回忆在哪里停留,问你自己吧。 :::

:::info[记事本联通乱码 (微软和联通有仇?)] 如果因为巧合导致 GBK 编码的字节流正好满足了 UTF-8 编码格式,那记事本会认为它是 UTF-8 从而乱码。Windows 旧版本的“联通”就是如此(新版本改了,但是这个 Bug 还在,只是“联”0xC1AA 是个超长的 UTF-8 编码而已)。你可以用你自己的名字或者其他什么词语去测试。 :::

:::info[烫烫烫、屯屯屯] Visual Code 中,Debug 模式下,未初始化的栈内存会被赋为 0xCC,堆内存赋为 0xCD。GBK 编码中,0xCCCC 为“烫”,0xCDCD 为“屯”。赶紧去检查一下数组越界和野指针吧。 :::

:::info[锘锘锘] 我们知道 UTF-8 没有字节序问题,但是 UTF-8 也仍然可以有 BOM,并且某些软件把它当成普通字符处理。U+FEFF 的 UTF-8 编码为 EF BB BF,其中 0xEFBB 在 GBK 下为锘,BF 会吞掉后一个字节。保存成无 BOM 即可,但是 Windows 的记事本一旦保存就有 BOM 。“锘縫ublic”“非法字符 65279”均属此类。 :::

:::info[一些生僻字夹杂日文] 如“浣犲ソ”,这是 UTF-8 编码的文本按 GBK 解码的结果,重新按 UTF-8 解码即可。 :::

:::info[不要以为原文是 ASCII 就不会乱码] ASCII 可能被记事本误以 UTF-16 解码,如 P2656 的某个数据点。这种情况显示的也是一些汉字。所以遇到乱码的时候多试几种编码。 :::

:::info[折半码] 乱码中有大量帽子字母和一些二分之一之类的分数。这是其它编码按 ISO-8859-1 解码的结果。如果带帽的 e 或 a 很多并且按 3 个为周期出现可以考虑为 UTF-8,其他时候一般考虑为 GBK ,不过还是可以多试几种编码。 :::

:::info[許功蓋] GBK、Big5 等编码的第二字节可能与 ASCII 冲突,当第二字节为 0x5C 的时候与 ASCII 反斜杠冲突,部分编译器不能识别,出现在注释里会导致下一行被注释掉。 :::

:::info[其他] GB 系列编码可能因为丢失一个字节而变为乱码,这个时候考虑用编辑器去掉第一个字节。 :::