计算机通信只要求比特送到;信息通信还要求两端对人看到的内容有共识。 同一句话用不同编码承载,字节数就不一样——这就是「多出来的一层麻烦」。
1.1.2 把通道打通了,这一节讲通道里跑的东西。 1.1.4 会回到「通道拿来干什么」:共享资源是网络最早、也是最主要的用途。
一、两种「通」的区别
「计算机通信」这个词在书里指早期那种点对点传数据:把一台机器上的比特原样搬到另一台机器上。 「信息通信」多了一层:搬过去的比特要能被理解——文字要认得出字,图像要认得出画面。
| 关注点 | 计算机通信 | 信息通信 |
|---|---|---|
| 成功标准 | 比特到齐 | 人能看懂 |
| 要协商什么 | 速率、校验 | 编码、格式、压缩方式 |
| 出错表现 | 丢包、错包 | 乱码、花屏、打不开 |
第三行那个「乱码」是最典型的症状:比特一个没丢,但两端用的码表不一样。 所以「信息通信」的排障和「计算机通信」完全不同:前者查编码声明,后者查链路质量。
二、编码决定字节数
同一段文字,用不同编码写出来长度不同,因为这个字符集里每个字符占几个字节不一样。
| 编码 | ASCII 字符 | 汉字 | 特点 |
|---|---|---|---|
| ASCII | 1 字节 | 编不出来 | 只有 128 个字符 |
| UTF-8 | 1 字节 | 3 字节 | 变长,兼容 ASCII |
| GBK | 1 字节 | 2 字节 | 中文专用,双字节 |
| UTF-16 | 2 字节 | 2 字节 | 定长,ASCII 也占 2 |
第二行那个 3 字节是 UTF-8 的关键设计:常用汉字落在三字节区,生僻字落在四字节区。 而 ASCII 只占 1 字节——所以「UTF-8 兼容 ASCII」这句话是字面意思: 一段纯英文文本,存成 UTF-8 和存成 ASCII,字节完全一样。 这也是 UTF-8 能取代 GBK 的技术原因之一:它不需要为英文单独优化,天生就省。
三、声明在哪里
接收端怎么知道该用哪张码表?靠声明。 声明的位置分三层:
| 层 | 写在哪 | 例子 |
|---|---|---|
| 传输层 | HTTP 头 | Content-Type: text/html; charset=utf-8 |
| 文档内部 | HTML 的 meta | <meta charset="utf-8"> |
| 文件开头 | BOM | EF BB BF 三个字节 |
第三行那个 BOM 是最容易出问题的一种:它在文件最前面塞三个看不见的字节,
有些程序(尤其是脚本解释器)会被它绊倒。
排障经验:看到「第一行明明是对的却报语法错」,先查有没有 BOM。
用十六进制看一眼头三个字节就知道——EF BB BF 就是它。
四、动手 Demo:从计算机通信到信息通信
把同一句话用四种编码写出来,比字节数,并检查能不能原样还原。 这段文字故意混了汉字和 ASCII,这样四种编码的差别才看得出来。
s = u'图解TCP/IP'
for enc in ('ascii', 'utf-8', 'gbk', 'utf-16-le'):
try:
b = s.encode(enc)
print('%-10s %2d 字节 %s' % (enc, len(b), b[:12].hex(' ')))
except UnicodeEncodeError:
print('%-10s 编不出来' % enc)
实跑输出:
--------------------------------------------------------------------
从计算机通信到信息通信
--------------------------------------------------------------------
**「计算机通信」关心的是把比特送到;「信息通信」关心的是送过去之后人能不能看懂。**同一句话,
用不同编码承载,字节数就不一样。
编码 字节数 前 12 字节 能还原
--------------------------------------------------------------------------
ascii — — 编不出来
utf-8 12 e5 9b be e8 a7 a3 54 43 50 2f 49 50 能
gbk 10 cd bc bd e2 54 43 50 2f 49 50 能
utf-16-le 16 fe 56 e3 89 54 00 43 00 50 00 2f 00 能
**同一段文字,ascii 直接编不出来、utf-8 要 12 字节、gbk 要 10 字节、utf-16 要 16 字节。**
**汉字越多,几种编码的差距越大**:utf-8 一个汉字占 3 字节,gbk 占 2 字节,
utf-16 一律 2 字节但每个 ASCII 字符也要 2 字节。
**这就是「信息通信」比「计算机通信」多出来的那一层麻烦**:
**比特送到了还不算完,两端得先说好用哪张码表。**
**HTTP 里那个 `Content-Type: text/html; charset=utf-8` 就是干这个的。**
ascii 那一行是「编不出来」,不是「编出来更短」——
「图解」两个汉字不在 ASCII 的 128 个字符里。
这一行最能说明「编码是一张表」:表里没有的字符,编码器只能报错,不能凭空造。
注意 utf-16-le 那行的前几个字节 fe 56 e3 89:
「图」的 Unicode 码位是 U+56FE,小端存放就变成 fe 56。
这也是为什么叫「小端」——低位字节放前面。
30 秒实验
- 把字符串换成纯英文
u'hello',看四种编码的字节数变成多少,验证「UTF-8 兼容 ASCII」这句话。 - 把字符串换成
u'龟'(一个四字节 UTF-8 的字?先查一下),看 utf-8 是不是真的变 4 字节。 - 试试
u'\ufeffhello'编码成 utf-8,看头三个字节是不是ef bb bf。
常见误区
- 以为 UTF-8 里一个汉字总是 3 字节。 常用字 3 字节,生僻字 4 字节,扩展区甚至更多。
- 以为 ASCII 是 UTF-8 的子集所以可以混用。 短文本没问题,长文本会有 BOM、行尾等差异, 混用迟早出事。
- 以为乱码是「数据坏了」。 乱码的数据往往一个字节没少,只是两端码表不一致。 改声明就能修好。
自测三问(点开对答案)
1. 为什么「UTF-8 兼容 ASCII」这句话是准确的? —— **因为 UTF-8 里 U+0000~U+007F 的编码就是一个字节,值与 ASCII 完全相同。** 纯 ASCII 文本存成 UTF-8,字节一个不差。 2. 同一句中文,UTF-8 和 GBK 哪个更短?为什么? —— **GBK 更短,因为汉字在 GBK 里固定 2 字节,在 UTF-8 里通常 3 字节。** 但 UTF-8 的 ASCII 部分省一半,**混排文本要看比例。** 3. 「第一行看起来正确却报语法错」,第一步查什么? —— **查文件头三个字节是不是 `EF BB BF`(BOM)。**很多解释器不认 BOM,会把它当成代码的一部分。下一节回到用途:通道有了、编码定了,网络最早被拿来干的事是「共享」。
- ☐ (本节完,下一节 1.1.4 计算机网络的作用)