一个汉字占几个字节?UTF-8、GBK、GB18030 和 UTF-16 对照

29 十月,2026 • 指南 • 64次浏览 • 1 分钟 阅读

书桌上的笔记本电脑旁摆着几排大小不一的木块,象征不同字符占用的字节数

常用汉字在 UTF-8 中占 3 个字节,在 GBK、GB18030 和 UTF-16 中占 2 个字节,部分扩展区汉字和大多数表情符号占 4 个字节。本文附实测对照表,并介绍如何用 Python、JavaScript 和在线工具计算字节数。

一个常用汉字在 UTF-8 编码下占 3 个字节,在 GBK、GB18030 和 UTF-16 下占 2 个字节。码位在 U+10000 以上的字符,例如扩展区汉字“𠮷”和大多数表情符号,在 UTF-8、UTF-16 和 GB18030 下都占 4 个字节,GBK 则根本无法编码这些字符。

英文字母、数字这类 ASCII 字符在 UTF-8、GBK 和 GB18030 下只占 1 个字节,换成 UTF-16 就是 2 个字节。所以同一段文字有多大,要看它用哪种编码保存或传输。接口、表单或数据库字段提示字节超限时,先弄清对方用的是哪种编码,再计算字节数。

为什么汉字在 UTF-8 中占 3 个字节

UTF-8 是变长编码,一个字符占 1 到 4 个字节,具体占几个由这个字符的码位决定。码位就是 Unicode 给每个字符分配的编号。截至 2026 年 10 月 29 日,定义 UTF-8 的 RFC 3629(2003 年 11 月发布,互联网标准 STD 63)规定的区间如下,“例子”一列是我们的实测结果。

码位范围UTF-8 字节数例子
U+0000 到 U+007F1(与 US-ASCII 相同)A、半角逗号 ,
U+0080 到 U+07FF2é
U+0800 到 U+FFFF(不含 U+D800 到 U+DFFF 代理项)3中、全角逗号 ,
U+10000 到 U+10FFFF4😀、𠮷(U+20BB7)

常用汉字位于 U+0800 到 U+FFFF 之间,所以在 UTF-8 里占 3 个字节。码位在 U+10000 以上的字符,包括部分扩展区汉字和大多数表情符号,占 4 个字节。

字节数只取决于码位,跟这个字常不常见没有关系。一个平时很少用的字,只要码位还在 U+FFFF 以内,在 UTF-8 下照样是 3 个字节。“𠮷”占 4 个字节,是因为它的码位 U+20BB7 超出了 U+FFFF,而不是因为它生僻。

其他编码各有各的规律。UTF-16 用一个 2 字节码元表示 U+FFFF 以内(代理项除外)的字符,码位高于 U+FFFF 的字符要用两个代理码元拼起来,也就是 4 个字节。GBK 能表示的常用汉字占 2 个字节,我们测试的全角逗号和感叹号也是 2 个字节,遇到它表示不了的字符会直接编码失败。在我们的测试里,GB18030 同样用 2 个字节存常用汉字,GBK 编不了的 😀 和 𠮷 它也能编码,各占 4 个字节。中国现行的强制性国家标准是 GB 18030-2022《信息技术 中文编码字符集》(2023 年 8 月 1 日实施),本文的 GB18030 字节数来自 Python 编码器的测试结果,并不代表对该编码器是否完全符合现行标准的验证。

常见字符和字符串的字节数对照表

常用汉字在 UTF-8 下占 3 个字节,在 GBK 下占 2 个字节,英文字母在这两种编码下都只占 1 个字节。下表是我们 2026 年 10 月 29 日用 Python 3.13 和 Node.js 实测的结果。字符数是 Python 中 len() 的返回值,也就是码位个数;UTF-16 列按小端序、不含 BOM 计算;最后一列是 JavaScript 字符串的 length 属性。

字符串字符数UTF-8GBKGB18030UTF-16JS length
A111121
中132221
中文264442
,(全角逗号)132221
,(半角逗号)111121
é122221
😀14无法编码442
𠮷(U+20BB7)14无法编码442
你好,世界!6181212126
Hello 世界8121010168
换行 \n(LF)111121
换行 \r\n(CRLF)222242
👨‍👩‍👧(家庭表情)518无法编码20168

从表里可以直接看出几条规律。“你好,世界!”的 6 个字符里有两个全角标点,在 UTF-8 下每个字符都是 3 个字节,合计 18 个字节,在 GBK 下合计 12 个字节。“Hello 世界”在 UTF-16 下是 16 个字节,反而比 UTF-8 的 12 个字节多,因为英文字母和空格在 UTF-16 里也要占 2 个字节。与 UTF-8 相比,ASCII 字符比例越高,UTF-16 的字节开销通常越大;如果文本以常用汉字为主,情况可能相反。

最后一行的家庭表情看上去只是一个图案,实际上由三个人物表情和两个零宽连接符组成,共 5 个码位。它在 UTF-8 下占 18 个字节,在 GB18030 下占 20 个字节,GBK 则无法编码。表单里只输入一个这样的表情,就可能用掉十几个字节。

字符数和字节数为什么对不上

算长度时要分清码位数、肉眼看到的字符数和字节数。一个汉字通常对应一个 Unicode 码位,但有些表情符号或带组合符号的文字由多个码位组成。本文对照表里的“字符数”按 Python 的 len() 计算,是码位数,不一定等于看到的字符个数。字节数则是这些码位编码后占多少存储空间。在 UTF-8、GBK 和 GB18030 下,纯 ASCII 文本的字符数和字节数相等,一旦出现汉字,两者就不再相等。更容易混淆的是,不同编程语言里的“长度”数的也不是同一种东西。

Python 的 len() 和 MySQL 的 CHAR_LENGTH() 数的是码位,JavaScript 的 length 数的是 UTF-16 码元。截至 2026 年 10 月 29 日,MDN 的 String.length 页面写道“length 数据属性表示字符串的 UTF-16 码元长度”,并举例说明 "😄".length 的结果是 2,而 [..."😄"].length 的结果是 1。

对常用汉字来说,这个差别一般不影响结果,“你好,世界!”无论用哪种方式数都是 6。可一旦文本里出现表情符号或扩展区汉字,几种数法的结果就会出现差异。还是拿前面的家庭表情来说,JavaScript 的 length 是 8,展开运算符 [...s].length 得到 5 个码位,UTF-8 字节数则是 18。

因此,看到“最多 100 个字符”或“最多 255 字节”这类限制时,要先分清限制的是字符还是字节,再弄清对方怎么数字符。如果前端用 length 校验、后端按字节校验,就可能出现前端放行、后端报错的情况。

用 Python、JavaScript 和 MySQL 计算字符串的字节数

Python 用 len(s.encode()),JavaScript 用 TextEncoder 或 Blob,MySQL 用 LENGTH(),都能算出字符串的字节数。下面的例子都先定义 s = "你好,世界!",结果可以和上面的对照表相互核对。

Python

str.encode() 会把字符串编码成 bytes,不传参数时默认使用 UTF-8,再用 len() 就能数出字节数。GBK 和 GB18030 的编码名可以在 Python 官方的标准编码列表里查到,gbk 的别名有 936、cp936 和 ms936。

写法结果含义
len(s)6码位个数
len(s.encode("utf-8"))18UTF-8 字节数
len(s.encode("gbk"))12GBK 字节数
len(s.encode("gb18030"))12GB18030 字节数
len(s.encode("utf-16-le"))12UTF-16 字节数(不含 BOM)

encode() 的 errors 参数默认为 "strict",遇到无法编码的字符会抛出 UnicodeError。所以对含有 😀 或 𠮷 的字符串执行 s.encode("gbk") 会直接报错,算不出字节数。

JavaScript

在浏览器和 Node.js 里,常用的写法有两种。根据 MDN 的 TextEncoder 页面,TextEncoder “接受码位流作为输入,并提供 UTF-8 字节流作为输出”,encode() 返回一个 Uint8Array,页面示例中 "€" 编码后的长度是 3。Blob 的 size 属性返回字节数,而按照 W3C 的 File API 规范,传给 Blob 的字符串会先经过 UTF-8 编码,所以 new Blob([s]).size 得到的同样是 UTF-8 字节数。

写法结果含义
s.length6UTF-16 码元个数
[...s].length6码位个数
new TextEncoder().encode(s).length18UTF-8 字节数
new Blob([s]).size18UTF-8 字节数

在我们的测试里,TextEncoder 和 Blob 两种写法对表中每个字符串给出的结果都与 UTF-8 列一致,中是 3,😀 是 4,家庭表情是 18。这两种写法只能算 UTF-8,需要 GBK 字节数时,可以改用上面的 Python 写法。

MySQL

根据 MySQL 8.4 参考手册,LENGTH() 返回字节数,CHAR_LENGTH() 返回字符数。在连接字符集为 utf8mb4 时,SELECT LENGTH('海豚'), CHAR_LENGTH('海豚'); 的结果是 6 和 2。至于 VARCHAR 字段能存多少个汉字,可以参阅MySQL varchar(255) 能存多少个汉字,这里不展开。

计算字节数时容易出错的几个地方

算出的字节数和预期不符时,常见原因包括编码选择、KB 换算、换行符和全角标点。另外,看起来相同的文字也可能有不同的 Unicode 规范化形式,比如“é”可以是一个码位,也可以是字母 e 加组合重音符号,两种写法的 UTF-8 字节数不同。

  • 用 GBK 处理表情符号或扩展区汉字。GBK 无法表示 😀、𠮷 这类字符,编码时会失败或报错。如果文本里可能出现这些字符,应改用能表示它们的编码,在我们的测试里 UTF-8 和 GB18030 都能编码。
  • 把 1000 和 1024 混用。有的工具按 1000 字节算 1 KB,有的按 1024 字节算 1 KB,同一段文字换算出的 KB 数会略有出入。比较两个结果之前,先看清各自的换算方式。
  • 忽略换行符的差别。不同来源的文本换行符可能不同,有的是 \r\n(CRLF),有的是 \n(LF)。前者每个换行在 UTF-8 下占 2 个字节,后者占 1 个字节,行数越多,差距越大。
  • 以为中文标点和英文标点一样大。全角逗号和感叹号的码位在 U+0800 到 U+FFFF 之间,在 UTF-8 下和常用汉字一样占 3 个字节,在我们的测试里 GBK 下占 2 个字节。半角的英文逗号在 UTF-8 下只占 1 个字节。

网址里的汉字为什么会变成 %E4%B8%AD

网址里的汉字会按 UTF-8 拆成字节,每个字节写成一个 %XX。“中”在 UTF-8 下是 3 个字节,放进网址就成了 %E4%B8%AD,“中文”则是 %E4%B8%AD%E6%96%87,正好 6 组。对一段中文做百分号编码后数一数有几组 %XX,就能直观看到它的 UTF-8 字节数。不过这只适用于单独编码的文本,不能凭整个网址里百分号的数量判断,因为网址里其他字符也可能被编码,各部分的规则也不同。想看看某段中文编码后的样子,可以使用 URL编码工具查看编码结果。

不写代码,用文本大小计算查看 UTF-8 字节数

文本大小计算是我们 Digily Link 提供的在线工具。它在浏览器中按 UTF-8 计算字节数,输入时结果会立即更新,文字不会发送到服务器,也无需登录。

  1. 打开文本大小计算页面。
  2. 在“文本”框中输入或粘贴要计算的内容。
  3. 查看下方的“大小”一行。页面上没有提交按钮,每输入或删除一个字,结果都会随之变化。

读结果时请注意以下几点。

  • 工具沿用 KB、MB 的名称,但按 1024 换算,结果中的 KB 相当于 1024 字节,MB 相当于 1024 KB。按标准单位写法,这对应 KiB 和 MiB。
  • 正好 1024 个字节时显示为 1024 B,超过 1024 个字节才换算成 KB,例如 1025 个字节显示为 1 KB。结果保留两位小数。
  • 粘贴“你好,世界!”后,“大小”一行应显示 18 B,与对照表中的 UTF-8 列一致。
  • 按照 HTML 规范,文本框读取内容时会把换行统一为 LF,所以即使从 Windows 文档里粘贴带 CRLF 的文字,每个换行通常也按 1 个字节计算。
  • 工具只计算 UTF-8。需要 GBK 或 GB18030 的字节数时,请使用前文的 Python 写法。

想核对一段文案、接口参数或表单内容是否超出字节限制,可以直接用文本大小计算查看整段文字的 UTF-8 字节数。

常见问题

生僻字一定是 4 个字节吗?

不一定,要看码位。在 Python 里运行 hex(ord("𠮷")) 会得到 0x20bb7,大于 0xFFFF,所以它在 UTF-8 下占 4 个字节。如果一个生僻字的码位在 0xFFFF 以内,它在 UTF-8 下仍然是 3 个字节。

字符串大小怎么换算成 KB?

先算出字节数,再根据所用的换算方式除以 1024 或 1000。举个假设的例子,一段文字的 UTF-8 字节数是 5120,按 1024 换算是 5 KB,按 1000 换算是 5.12 KB。

为什么保存成文件后,大小和计算结果不一样?

保存文件时,编辑器可能会在开头加上 BOM,或把换行写成 CRLF,有些文件格式还会加入自身的结构信息,所以文件大小和文本本身的字节数不一定相同。

0 个评分中的 0 个