前言
最近在 Go 处理字符串时发现对计算机处理文本的原理不是很透彻,因此翻阅更多教学文件了解计算机如何处理文本。
历史上计算机如何处理文本?
- 计算机存储与运算都是通过数字,而文本是由一系列数字进行编码
- 早期计算机厂商有各自的标准(EBCDIC)
- ASCII(美国信息交换标准码)统一了英文常用的字符,采用 7 bit 来记录字符,并保留最高的 bit 用作奇偶校验位(parity bit),受限于当时传输错误率高且没有健全的协议处理
- 第 8 个 bit 之后也被不同标准扩充用于解释不同字符,但也带来标准混乱导致“乱码”的问题
更多字符扩充的需求
但像是汉字有上千上万个字符要如何应对呢?新标准:万国码 Unicode,通过替世界上每个字符指派对应的码位 Codepoint来解决。
UTF-8 vs UTF-16 vs UTF-32
前面提到的都是“某个数字对应某个字符”的标准,但具体来说文本应该如何被存储?Unicode Transformation Format 简称 UTF,其中数字代表存储的最小 bit 单位。
- 用过大的空间去存储字符是不经济的
- 新的存储格式标准要向后兼容 ASCII
- 已经有太多程序基于 8 bit 编码开发
基于以上考量,UTF-8 成为目前网络上最流行的编码格式。
UTF-32 的特点:
- 每个字符都用 4 个 Byte 表示
- 不兼容 ASCII
- 处理简单,可以直接通过索引访问第 n 个字符
- 浪费空间,很少被实际使用
UTF-16 的特点:
- 每个字符都用 2 或 4 个 Byte 表示
- 不兼容 ASCII
- 大部分常用字符用 2 个 Byte 表示
- JavaScript、Java 等语言内部采用此编码
const s = "😀"; // emoji 超出了 16 bit 范围console.log(s.length); // 2 (不是 1!)UTF-8 的特点:
- 每个字符都用 1 ~ 4 个 Byte 表示
- ASCII 兼容:对于 ASCII 字符只用 1 个 Byte,且编码完全相同
- 自同步性(Self-synchronization):可以从任意位置开始解码,因为每个 Byte 的前几个 bit 会标示这是单独字符还是多 Byte 字符的一部分
我现在在字符的哪个位置看到 0xxxxxxx → 「我在一个单字节字符」看到 110xxxxx → 「我在一个双字节字符的开头」看到 1110xxxx → 「我在一个三字节字符的开头」看到 11110xxx → 「我在一个四字节字符的开头」看到 10xxxxxx → 「我在多字节字符的中间某处」延伸阅读
- UTF-8, Explained Simply - Nic Barker
- If You Can’t Explain UTF 8 vs Unicode, Watch This - LearnThatStack
- 一起成为新世纪文字艺术师:深入玩转 Unicode 和 OpenType 系列 - chingru
- How Computers Store Text - ASCII, Unicode, UTF-8, UTF-16, and UTF-32 - NoBS Code