Skip to main content

ASCII、Unicode、UTF-8:字符编码进化史

这三者其实是一部计算机字符编码的进化史

要搞懂它们,你需要区分两个核心概念:

  1. 字符集 (Character Set):把字符映射成数字的列表(比如:'A' 是 65,'中' 是 20013)。
  2. 字符编码 (Encoding):把这些数字真正存进计算机(二进制)的规则。

1. ASCII:早期的美国标准 (1960s)

全称:American Standard Code for Information Interchange(美国信息交换标准代码)。

  • 背景:计算机刚发明时,主要是美国人在用。他们只需要英语字母、数字和一些标点符号。
  • 原理:使用 7个 bit (位) 来表示一个字符。也就是说,ASCII 最多只能存 128 个字符(2^7 = 128)。
  • 内容
    • 0-31:控制字符(换行、回车、响铃等)
    • 32-127:可打印字符(a-z, A-Z, 0-9, !@#$ 等)
  • 缺点完全不考虑非英语国家。后来欧洲人来了,发现没有重音符号(e, u);中国人来了,发现几万个汉字根本放不下。

2. 混乱时代:OEM 与 ISO-8859

为了解决 ASCII 不够用的问题,各国开始"魔改":

  • 利用剩下的第 8 位(128-255)来存自己的符号
  • 中国搞出了 GB2312、GBK(用两个字节存一个汉字)
  • 台湾搞出了 Big5

问题(乱码之源):同一个数字 200,在法语系统里可能是 e,在俄语系统里可能是个俄文。同一个文件跨国打开全是乱码(Mojibake)。


3. Unicode:大一统的"万国码" (1990s)

全称:Universal Coded Character Set。

  • 使命:给世界上所有的符号(包括汉字、Emoji、楔形文字)都分配一个独一无二的编号
  • 本质:它只是一个字符集(列表),它只规定了"哪个字符对应哪个数字",并没有规定怎么存
  • 码点 (Code Point):Unicode 给每个字符分配的那个十六进制编号,通常写成 U+XXXX
    • A -> U+0041
    • -> U+4E2D
    • ... -> U+1F4A9

问题:Unicode 收录了 100 多万个字符,最大的字符可能需要 4 个字节才能装下。如果所有字符都强制用 4 个字节存(比如 UTF-32):

  • A 本来只需要 1 个字节,现在也要占 4 个字节(前面补 3 个字节的 0)
  • 结果:对于全是英语的文档,文件体积直接变成 4 倍。太浪费硬盘和带宽了!

4. UTF-8:最聪明的"打包工"

全称:Unicode Transformation Format - 8-bit。

  • 背景:为了解决 Unicode 存储浪费空间的问题,Ken Thompson(没错,就是发明 Go 语言和 Unix 的那个大神)在一个餐垫上设计出了 UTF-8
  • 本质:它是 Unicode 的一种实现方式(编码)
  • 特点:可变长度(Variable Width)。它看人下菜碟:
    • 遇到 ASCII 字符(0-127):只用 1 个字节 存(和 ASCII 一模一样)
    • 遇到欧洲字符:用 2 个字节
    • 遇到常用汉字(如"中"):用 3 个字节
    • 遇到生僻字或 Emoji:用 4 个字节

UTF-8 二进制规则

计算机怎么知道这几个字节是属于一个字的,还是分开的三个字?看前缀(Header)。

字符范围 (16进制)需要字节数二进制模板 (x 代表实际填入的数据)解释
0000 - 007F1 byte0xxxxxxx首位是0,兼容 ASCII
0080 - 07FF2 bytes110xxxxx 10xxxxxx110开头,表示这是双字节字的头
0800 - FFFF3 bytes1110xxxx 10xxxxxx 10xxxxxx1110开头,表示这是三字节字 (汉字都在这)
10000 - ...4 bytes11110xxx 10xxxxxx ...Emoji 等

Followers: 后面跟着的字节都必须以 10 开头。这样即使数据流断了,计算机也能很容易找到下一个字符的起始位置(具有自同步能力)。


5. 举个栗子:对比 "A" 和 "中"

字符 'A'

  • ASCII: 0x41 (1 byte)
  • Unicode: U+0041
  • UTF-8: 0x41 (1 byte)
  • 优势:和 ASCII 完全一样,老软件也能读

字符 '中'

  • Unicode: U+4E2D (二进制: 0100 1110 0010 1101)

  • UTF-8: 需要 3 个字节

    1. 填入模板: 1110xxxx 10xxxxxx 10xxxxxx
    2. 4E2D 的二进制切分填进去
    3. 结果(16进制):E4 B8 AD
  • 所以在 Go 里,len("中") 等于 3


6. 总结

名称角色关键点优缺点
ASCII鼻祖只有 128 个字符只能存英文,太小了
Unicode地图 (字符集)给万物编号 (U+XXXX)统一了标准,但没规定怎么存
UTF-8打包工 (编码)变长编码,Unicode 的一种存法Go 的默认编码。省空间,兼容 ASCII

7. 与 Go 语言的关系

在 Go 语言中:

  • string 底层就是 UTF-8 字节数组
  • rune 类型本质上就是 Unicode 码点(即 int32),用来处理那个 U+XXXX 的数字
s := "中"
len(s) // 3 (UTF-8 字节数)
len([]rune(s)) // 1 (Unicode 字符数)

相关阅读