← 一些知识

古旧铁轨与万国文字的车厢:ASCII 与 UTF-8 编码的传承、困境与全球方案

棱镜2026.5.30  ·  日志

为什么英文字母在计算机里只占 1 个字节,中文却需要 3 个?为什么同一个文件在 Windows 上打开是正常的、在 macOS 上就变成一片乱码?从 ASCII 到 UTF-8,这场编码体系跨越了半世纪——它是技术问题,但背后是不同语言的困境、非英语国家的挣扎、以及一次罕见的全球妥协如何被设计出来。

🔑 关键词索引

关键词一句话说明
ASCII1963 年诞生的 7 比特编码,128 个字符,覆盖英文字母、数字和标点——计算机世界的铁轨轨距
7 比特与第 8 位ASCII 只用 7 bit,第 8 位早期用作奇偶校验——后来被各国用来扩展自己的字符,导致了编码混乱
控制字符(0~31)不可打印的指令性字符——换行、回车、响铃、ESC——来自电报和终端的遗产
⭐ 编码地狱同一字节在不同编码中对应不同字符——0xC4 在 GB2312 里是「你」,在 Latin-1 里是「Ä」——打开文件靠猜
Unicode1987 年启动的全球统一字符表,给每个字符一个唯一的码点(U+XXXX),但只管编号不管存储
⭐ UTF-8可变长编码方案(1~4 字节),向后完全兼容 ASCII,向前覆盖全球所有文字——Web 的事实标准
自同步UTF-8 的精妙设计——任意字节位置都能判断是否为一个字符的起始,从数据中间切入也不会错乱
BOM(Byte Order Mark)U+FEFF 放在文件开头标记字节序——UTF-16 必需,但被滥用进 UTF-8 后造成了 Shell 脚本灾难
NFC 与 NFD同一个字符「é」可以编码为单个码点(NFC)或分解为「e+重音符」(NFD)——macOS 用 NFD,Linux 用 NFC,git 因此发疯
拉丁扩展与 CJKUTF-8 中:ASCII 字符 1 字节,拉丁扩展(希腊文、西里尔文)2 字节,中日韩汉字 3 字节,表情符号 4 字节
GB2312 → GBK → GB18030中国编码标准的三代演进:从 6763 个常用汉字到覆盖全部 Unicode 汉字——直至被 UTF-8 统一
「ñ」灾难最经典的编码 Bug:UTF-8 编码的 ñ(C3 B1)被 Latin-1 解码为 Ã(C3)和 ±(B1)——浏览器和服务端编码不一致的后果
Emoji 的字节膨胀一个表情符号在 UTF-8 中占 4 字节,由 ZWJ 连接的多人物家庭表情可达 25 字节——「一条微博」的字数限制因此变得复杂
chardetMozilla 开发的编码探测库——在没有元数据时通过统计特征猜测文件编码,但不是 100% 准确的
Percent-encodingURL 编码——非 ASCII 字节以 % 前缀加两位十六进制表示,浏览器的地址栏专用方案
Base64将二进制数据映射到 64 个可打印 ASCII 字符的传输编码,邮件附件和 JWT 的标准方案
Punycode中文域名的底层编码方案——「例子.测试」在 DNS 里是 xn--fsq.xn--0zwm56d
Tokenization与字符编码不同的抽象层——LLM 将文本切分为语义单元的方式,中文字符通常被切得更碎

一、七根轨道:ASCII 的诞生与它的边界

一个每个人都遇到过的问题

为什么英文字母只占 1 个字节,中文汉字要占 3 个?同一个文件在 Windows 和 Mac 上打开,为什么有时候会变成乱码?如果计算机只能理解 0 和 1,那么「A」和「中」在它眼中是什么?这些问题的答案藏在两代编码方案的传承与断裂中。

ASCII 的诞生:当计算机需要一张字母表

1963 年,美国标准协会(后来的 ANSI)推出了 ASCII(American Standard Code for Information Interchange)。在此之前,每家计算机公司各用各的字符编码——IBM 用 EBCDIC,其他厂商各有各的标准——数据交换就是一场噩梦。ASCII 的目的很简单:让不同厂商的计算机,能读懂同一套字母表。

它的设计方案是 7 个比特(bit),可以表示 2⁷ = 128 个字符。为什么是 7 而不是 8?因为当时数据以 8 位字节传输,第 8 位被保留作为 奇偶校验位——一个检错机制。后来这个第 8 位被各国用来扩展自己的字符,正是后来「编码地狱」的根源。

ASCII 字符全集(0~127)

以下是从 NULL(0)到 DEL(127)的完整 ASCII 表,分为控制字符和可打印字符两大部分。

控制字符区:0~31 + DEL(127)

这些字符不可打印,来自电报和早期终端的操作指令。虽然今天普通用户很少直接接触它们,但它们深刻影响了现代计算机的底层。

十进制十六进制二进制缩写名称说明
0000000000NUL空字符C 字符串结尾标记;数据流填充
1010000001SOH标题开始早期电报/消息的头部标记
2020000010STX正文开始SOH 之后的实际数据开始
3030000011ETX正文结束数据段结束标记
4040000100EOT传输结束整个传输会话终止
5050000101ENQ询问请求对方响应(如「还在吗?」)
6060000110ACK确认数据正确接收的肯定答复
7070000111BEL响铃触发终端发出声音——现代人早就听不到了
8080001000BS退格光标回退一格
9090001001TAB水平制表符跳至下一个制表位——表格对齐的功臣
100A0001010LF换行Unix/Linux 的行结束符;光标下移一行
110B0001011VT垂直制表符光标下移至下一个垂直制表位
120C0001100FF换页打印机弹出一页纸;终端清屏
130D0001101CR回车光标回到行首;Windows 用 CR+LF 作为换行符
140E0001110SO移出切换至另一字符集(如扩展图形)
150F0001111SI移入从 SO 切换回标准字符集
16~1910~13001xxxxDLE~DC3数据链路转义 / 设备控制 1~3通信协议控制 / 外围设备开关
20140010100DC4设备控制 4外围设备控制(停止/断开)
21~2315~170010101~0010111NAK~ETB否定确认 / 同步空闲 / 块传输结束通信协议的控制信号
24180011000CAN取消忽略之前传输的数据
25190011001EM介质结束磁带/磁盘等物理介质用尽
261A0011010SUB替换替换有错误的字符;Ctrl+Z 作为 Windows 文本文件结束标记
271B0011011ESC转义引入后续字节的替代解释——Vim 中按 ESC 退出编辑模式
28~301C~1E0011100~0011110FS~RS文件/分组/记录/单元分隔符层级数据分隔体系
311F0011111US单元分隔符分隔体系的最小单元
1277F1111111DEL删除纸带打孔时全部打穿=删除该字符

可打印字符区:32~126

这是 ASCII 人人皆知的另一半——标准美式键盘能打出来的所有字符。一个经典细节:'A' 是 65(十进制),'a' 是 97,大小写之间正好差 32,也就是一个空格的数值。 这意味着大写锁定本质上就是第 5 比特位的翻转。

十进制十六进制二进制字符说明
32200100000(空格)空格——文本中最常用的「字符」
33~3821~260100001~0100110! " # $ % &标点符号组 1:感叹、引号、井号、美元、百分、和号
39~4727~2F0100111~0101111' ( ) * + , - . /标点符号组 2:单引、括号、星号、加号、逗号、减号、句点、斜杠
48~5730~390110000~01110010 1 2 3 4 5 6 7 8 9数字——48 是 '0' 的起点,'9' 是 57
58~643A~400111010~1000000: ; < = > ? @标点符号组 3:冒号、分号、小于、等于、大于、问号、At 符号
65~9041~5A1000001~1011010A B C D ... X Y Z大写英文字母——'A'=65,'Z'=90
91~965B~601011011~1100000[ \ ] ^ _ `标点符号组 4:方括号、反斜杠、闭方括号、脱字符、下划线、反引号
97~12261~7A1100001~1111010a b c d ... x y z小写英文字母——'a'=97,'z'=122,恰好比大写多 32
123~1267B~7E1111011~1111110{ | } ~标点符号组 5:花括号、竖线、闭花括号、波浪号

这是 ASCII 的全部——95 个可打印字符 + 33 个控制字符 + DEL。这就是 128 个位置能容纳的一切。对于 1960 年代的美国,这些够用了。但全世界远不止这些文字。

二、编码的巴别塔:ASCII 之外的世界怎么办

第 8 位的争夺与扩展 ASCII 的乱局

当计算机走出英语世界,第一个问题就来了:ASCII 里没有 é、没有 ü、没有 ñ、没有中文、没有日文、没有阿拉伯文。怎么办?

最自然的做法是使用被空置的 第 8 位——把 ASCII 的 7 位扩展到 8 位,多出 128 个位置(128~255)。不同的国家、不同的组织,把这 128 个位置填上了各自需要的字符。

于是产生了数十种互不兼容的 扩展 ASCII 标准:

编码覆盖地区说明
Latin-1(ISO-8859-1)西欧最成功的扩展 ASCII——覆盖法语 é、德语 ü、西班牙语 ñ 等;HTML 的默认编码之一
Latin-2(ISO-8859-2)东欧覆盖波兰语、捷克语、匈牙利语等中欧语言
ISO-8859-5西里尔字母俄语、乌克兰语等
ISO-8859-7希腊语
ISO-8859-8希伯来语
Shift_JIS日本日文编码——ASCII + 平假名/片假名 + 汉字(2 字节)
EUC-KR韩国韩文编码
GB2312中国大陆1980 年标准,6,763 个汉字 + 符号——解决了「有没有中文」的问题
Big5(大五码)台湾/港澳13,053 个汉字——和 GB2312 完全不兼容

编码地狱

这就是所谓 ANSI 乱码问题——一个字节 0xC4:

在 GB2312 里是「你」
在 Latin-1 里是「Ä」
在 Shift_JIS 中是某个日文字符的组成部分
在 Big5 里——可能根本不是一个合法字符

你用错误的编码打开一个文件,屏幕上呈现的不是"你"而是"Ä"——不是因为数据损坏了,是因为解读数据的钥匙用错了。在那个年代,打开一个来自陌生国家的文本文件就像一场轮盘赌。浏览器需要同时尝试七八种编码,看看哪个渲染出来的汉字最多、乱码最少。

三、一份万国通用的字母表:Unicode 的调和方案

一个想法:给每个字符一个唯一的身份证号

1987 年,几家大公司的工程师坐在一起,决定终结这场编码混战。方案非常直接:给全世界每一种文字的每一个字符分配一个全局唯一的编号。这个编号叫做 码点(code point),写作 U+XXXX

字符码点所属区块
AU+0041基本拉丁字母(与 ASCII 完全一致)
U+4E2DCJK 统一表意文字
éU+00E9拉丁补充-1
😊U+1F60A表情符号(Emoticons)
U+3042平假名
πU+03C0希腊字母
𝄞U+1D11E音乐符号(高音谱号)

截至 2026 年,Unicode 已收录超过 15 万个字符,覆盖 168 种文字。但 Unicode 只是一个字符表——它只解决了「每个字符叫什么名字」,没有解决「怎么把它存成字节」。于是出现了几种不同的存储方案。

存储方案的三角难题

方案编码方式优点缺点
UTF-32每个码点固定 4 字节简单粗暴,O(1) 定位英文字母从 1 字节→4 字节,空间膨胀 4 倍
UTF-16常用字符 2 字节,生僻 4 字节中文也是 2 字节,对东亚友好不兼容 ASCII;BOM 问题;代理对增加复杂度
UTF-8变长 1~4 字节,按使用频率分配长度兼容 ASCII;无字节序问题;自同步中文需要 3 字节(比 UTF-16 多 50%)

UTF-32 因为太浪费几乎没人用。剩下两个——UTF-16 vs UTF-8——是真正的战场。Windows NT 内核选 UTF-16,互联网选 UTF-8。结果是 UTF-8 赢了。

四、变长的艺术:UTF-8 的精妙设计

编码规则:通过头部字节的自描述结构

UTF-8 的设计可以浓缩为一张表和两条规则。这张表是它的灵魂:

字节数有效比特位码点范围模板模式覆盖内容
17U+0000~U+007F0xxxxxxxASCII 全部 128 个字符——完全兼容
211U+0080~U+07FF110xxxxx 10xxxxxx拉丁扩展(希腊文、西里尔文、阿拉伯文等)
316U+0800~U+FFFF1110xxxx 10xxxxxx 10xxxxxxCJK 汉字、韩文、常用符号——覆盖绝大多数日常文字
421U+10000~U+10FFFF11110xxx 10xxxxxx 10xxxxxx 10xxxxxx表情符号、古文字、罕见汉字、音乐符号

两条规则同样重要:

这两条规则赋予了 UTF-8 一个 ASCII 没有、UTF-16 也没有的特性:自同步。无论你在字节流中从哪个位置开始读取,扫描几个字节就能找到下一个字符的起始位置。即使数据从中段损坏,损失也仅限于当前字符,不会像其他编码那样后续全部错位。

具体字符的编码实例

看三个典型字符,覆盖 1 字节、3 字节、4 字节三种情况:

'A' → U+0041 → 7 位就放得下 → 1 字节模板 0xxxxxxx → 01000001 = 0x41
      → 结果:1 个字节,和 ASCII 的 'A' 字节完全一致
'中' → U+4E2D → 超出了 2 字节上限(07FF)→ 3 字节模板

  U+4E2D = 0100 1110 0010 1101(16 位二进制)
  填入 1110xxxx 10xxxxxx 10xxxxxx
      = 11100100 10111000 10101101
      = E4 B8 AD
      → 结果:3 个字节(对照:GB2312 中「中」只需要 2 字节)
'😊' → U+1F60A → 超过了 3 字节上限(FFFF)→ 4 字节模板

  U+1F60A = 0001 1111 0110 0000 1010(21 位二进制)
  填入 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
      = 11110000 10011111 10011000 10001010
      = F0 9F 98 8A
      → 结果:4 个字节

为什么 UTF-8 赢了

特性UTF-8UTF-16ASCII
向后兼容 ASCII✅ 完全兼容——0x41 就是 'A'❌ 不兼容——'A' 存为 00 41 或 41 00
无字节序问题✅ 字节序无关——字节有明确的头部标识❌ 需要 BOM——大端/小端让人头疼✅ 没问题(单字节)
自同步✅ 任何字节位置都能识别字符边界❌ 无法自同步——16 位边界可能错位❌ 无法自同步(混在扩展 ASCII 中时)
空间效率(英文)⭐ 最优——1 字节❌ 浪费——2 字节⭐ 最优——1 字节
空间效率(中文)尚可——3 字节⭐ 最优——2 字节❌ 不支持
错误检测✅ 有——后续字节不以 10 开头则数据损坏❌ 无❌ 无

UTF-8 的「兼容 ASCII」不是一种妥协——它是一种对历史的尊重和对迁移成本的精确计算。 世界上有无数的 ASCII 文件、C 源代码、Unix 配置文件——UTF-8 让它们不需要任何转换就自动成为了合法的 Unicode 文件。没有这个兼容性设计,UTF-8 不可能成为互联网事实标准。

五、ASCII 与 UTF-8 全方位对照总表

基础特性对照

维度ASCIIUTF-8
全称American Standard Code for Information InterchangeUnicode Transformation Format — 8-bit
诞生年份1963(第一版)1993(RFC 2044,后合并入 RFC 3629)
制定者美国标准协会(ANSI)Ken Thompson & Rob Pike(贝尔实验室)
编码方式固定 7 比特/字符可变 1~4 字节/字符
最大字符数1281,112,064(有效 Unicode 码点)
英文字母1 字节(如 'A' = 0x41)1 字节(完全一致,也是 0x41)
中文字符❌ 不支持3 字节(如 '中' = E4 B8 AD)
表情符号❌ 不支持4 字节(如 '😊' = F0 9F 98 8A)
向后兼容完全兼容 ASCII(U+0000~U+007F)
字节序依赖无(单字节)无(自描述)
自同步能力
错误检测有(无效字节序列可被识别)
空字节(NUL)0x000x00(与 ASCII 一致,但只在 1 字节范围内)

字节长度对照表(按字符类型)

字符示例字符名称Unicode 码点ASCII 字节数UTF-8 字节数UTF-8 实际字节
A拉丁大写 AU+00411141
~波浪号U+007E117E
é带重音的 eU+00E92C3 A9
ñ带波浪线的 nU+00F12C3 B1
α希腊小写 alphaU+03B12CE B1
π希腊小写 piU+03C02CF 80
Д西里尔大写 DeU+04142D0 94
CJK 汉字「中」U+4E2D3E4 B8 AD
CJK 汉字「文」U+65873E6 96 87
平假名「あ」U+30423E3 81 82
😊微笑表情U+1F60A4F0 9F 98 8A
🚀火箭表情U+1F6804F0 9F 9A 80
𝄞高音谱号U+1D11E4F0 9D 84 9E

一段文本的编码效果对比

取一段短文本"Hello 世界 🌍",看三种编码的存储差异:

字符ASCII 字节UTF-8 字节UTF-16(小端)字节
H484848 00
e656565 00
l6C6C6C 00
l6C6C6C 00
o6F6F6F 00
(空格)202020 00
E4 B8 9616 4E
E7 95 8C8C 75
(空格)202020 00
🌍F0 9F 8C 8D0D F0 0D F0(代理对)
总计7 字节(只算英文+空格,中文和表情不支持)17 字节22 字节(含代理对)

注意:ASCII 直接拒绝中文和表情符号。UTF-8 只需要 17 字节就能表示所有字符——而且 7 个英文字符+空格共 8 字节与 ASCII 完全一致。这是它最优雅的地方。

六、暗流与碎片:编码世界的延伸战场

UTF-16 的 BOM 战争

在 UTF-16 的世界里,两个字节的顺序是一个根本问题:

字节序'A' 的存储主要使用者
大端(BE)00 41(高位在前)网络协议(TCP/IP 用大端,所以也叫"网络字节序")
小端(LE)41 00(低位在前)x86 架构、Windows

为了解决这个问题,Unicode 标准引入了 BOM(Byte Order Mark)——在文件开头放一个不可见的字符 U+FEFF(零宽不换行空格),告诉解码器我是什么字节序:

UTF-16 BE 文件开头:FE FF → 后续字节按大端解读
UTF-16 LE 文件开头:FF FE → 后续字节按小端解读

这引发了一个更复杂的问题:有人把 BOM 引入了 UTF-8。UTF-8 根本没有字节序问题,但 Windows 记事本在保存 UTF-8 文件时,默认在开头加上了 EF BB BF——这就是 UTF-8 BOM。结果:

你在 Windows 记事本里写了一个 Shell 脚本,保存为 UTF-8。
传到 Linux 服务器上,运行。
#!/bin/bash 前面多了 3 个不可见字节。
Shell 报错:bad interpreter: No such file or directory

这就是经典的 Notepad BOM 灾难。20 年来,无数开发者因为这个原因踩过坑。

NFC 与 NFD:同一个字符的两副面孔

看这个字符:é(带重音的 e)。

在 Unicode 的世界里,它有两种表示方式:

形式组成码点UTF-8 字节长度
NFC(预组合)单个字符 éU+00E9C3 A92 字节
NFD(分解)字符 e + 组合重音符号U+0065 + U+030165 CC 813 字节

视觉上完全一样。计算机看来——完全不一样。这就是 Unicode 归一化问题,最经典的受害者是跨平台文件共享:

macOS 的文件系统(HFS+ / APFS)默认将文件名存储为 NFD 形式。
Linux 用 NFC。

你在 Mac 上创建一个文件名叫「café.txt」。
git 提交。同事用 Linux 拉下来。
他的 git status 显示:「café.txt」被修改了。
但你明明没改过。

原因:你在 Mac 上创建的是 NFD 版(cafe + 重音),
同事的 Linux 自动转成了 NFC 版(预组合 é)。
git 认为这是两个不同的文件名。

中文编码的三代进化

中文进入计算机的过程远比其他语言艰难——英文只需要 26 个字母,中文光常用字就六七千。

标准年份汉字数编码方式说明
GB231219806,7632 字节第一代简体中文标准,覆盖常用字,但生僻姓名打不出来
GBK199321,0032 字节扩展 GB2312,兼容原编码——「镕」字终于有了(朱镕基总理的名字问题)
GB180302000(2005 强制)全部 Unicode 汉字变长 1/2/4 字节中国强制标准,向后兼容 GBK,覆盖全部 CJK 扩展

同时,台湾和港澳走的是完全不同的 Big5(大五码),和 GB 系列互不兼容。一段 Big5 文本在 GBK 解码器下看到的是一堆乱码——「我」变成「¦Ú」——直到 UTF-8 成为通用方案才终结了这场持续三十年的编码内战。

「ñ」灾难:Web 开发中最经典的编码 Bug

这是 Web 开发中最经典的教学案例,涉及三处编码不匹配:

你在 HTML 表单中输入「ñ」,提交。
  ↓
浏览器用 UTF-8 编码发送:C3 B1
  ↓
服务端误以为用 Latin-1 接收:
    把 C3 解读为 Ã(U+00C3)
    把 B1 解读为 ±(U+00B1)
  ↓
数据库里存下了「ñ」
  ↓
渲染出来就是「ñ」

修复方法:确认三处编码一致——页面声明的 charset、HTTP 响应头的 Content-Type、数据库连接字符集——全部设为 UTF-8。少一处都不行。

Emoji 的字节膨胀

一个表情符号已经是 4 字节了。如果是由多个表情用 ZWJ(零宽连接符,U+200D) 连接起来的家庭组合表情呢?

「👨‍👩‍👧‍👧」(一家四口)

👨 U+1F468(4 字节)
  ‍ U+200D(3 字节)
👩 U+1F469(4 字节)
  ‍ U+200D(3 字节)
👧 U+1F467(4 字节)
  ‍ U+200D(3 字节)
👧 U+1F468(4 字节)

→ UTF-8 下共 25 个字节

这就是为什么 Twitter 和微博的「字数限制」和编码有关——一条 140 字符的推文中如果混入大量 emoji,实际存储开销远不止 140 字节。

七、编码一致性的黄金法则

三个必须一致的环节

无论你做的是 Web 开发、数据库设计还是文件处理,以下三处编码必须一致

声明 ≠ 实际 ≠ 存储——任何一处的编码不匹配,都会导致乱码。

环节Web 开发数据库文件处理
声明<meta charset="UTF-8">表的 COLLATION文件头 BOM / .gitattributes
传输HTTP Content-Type: charset=utf-8连接字符集(SET NAMES utf8mb4HTTP 响应头 / 流编码
实际内容编辑器的保存编码实际存储的二进制字节文件本身的字节序列

在 Web 开发中,一个最省心的原则是:从浏览器到服务器到数据库,全链路 UTF-8。

编码探测工具

当你拿到一个没有编码声明的文件时,怎么猜它的编码?Mozilla 的 chardet 库是目前最好的探测工具,它的策略是:

但它不是 100% 准确的。唯一的可靠方案就是明确标注编码

八、补充专题:URL 编码、Base64 与不同场景下的编码方案

Percent-encoding:URL 中的中文

你在浏览器地址栏输入「https://example.com/中文」,按下回车后——浏览器会自动将其转换为 https://example.com/%E4%B8%AD%E6%96%87。这就是 Percent-encoding(URL 编码)。它的规则很简单:每个非 ASCII 字节用 % 前缀 + 两位十六进制表示。%E4%B8%AD 就是「中」字 UTF-8 编码的三个字节。浏览器的地址栏只接受 ASCII 字符集,一切超出范围的字符都必须这样转义才能传输。

Base64:用 ASCII 传二进制

邮件附件、图片的 Data URI、JWT Token——这些场景有一个共同点:需要在只允许 ASCII 字符的传输通道里传递任意二进制数据。Base64 将每 3 个字节(24 位)拆成 4 组 6 位,每组映射到一个可打印的 ASCII 字符(A-Z、a-z、0-9、+、/)。效果就是二进制变成了看起来像乱码的 ASCII 文本。代价是体积膨胀约 33%。

Punycode:中文域名的工作原理

当你访问「例子.测试」这样的中文域名时,DNS 系统只理解 ASCII。解决方案是 Punycode——将 Unicode 字符编码为 ASCII 兼容的字符串,以 xn-- 开头。例如「例子.测试」在 DNS 层面实际上是 xn--fsq.xn--0zwm56d。浏览器在地址栏显示给人看的是中文,发给 DNS 服务器的是 Punycode。

字符编码与 tokenization:两个不同层

字符编码(ASCII、UTF-8)解决的是「字符怎么变成字节」,而 LLM 的 tokenization 解决的是「文本怎么切分成模型理解的语义单元」。两者在完全不同的抽象层上工作:

「Hello world」→ 11 个字符 → 11 字节(UTF-8)→ 约 3~4 个 token
「你好世界」→ 4 个字符 → 12 字节(UTF-8)→ 约 4~6 个 token

一个 token 和字节之间没有固定比例。中文每个字在主流 tokenizer 中通常被切得更碎——同一个意思的表达,中文产生的 token 数通常是英文的 1.5~2 倍。这也是为什么中文场景下 token 消耗比英文更快。

待延伸线索