Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2.2 ASCII与扩展编码

ASCII是最早的字符编码标准,也是所有现代编码的基础。这一节我们详细讲解ASCII编码以及后续的各种扩展编码。

ASCII编码详解

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是1963年由美国国家标准学会制定的编码标准,主要用于显示现代英语和其他西欧语言。

ASCII编码的特点

  • 总共包含128个字符,用7个二进制位表示
  • 其中0-31和127是控制字符(如换行、回车、删除等)
  • 32-126是可打印字符,包括英文字母、数字、常用符号

ASCII码表结构

码位范围字符类型说明
0-31控制字符不可打印,用于控制设备
32空格可打印
48-57数字0-9可打印
65-90大写字母A-Z可打印
97-122小写字母a-z可打印
其他标点符号、特殊符号可打印
127删除控制符控制字符

示例:

  • ‘A’ → 65 → 二进制 01000001
  • ‘a’ → 97 → 二进制 01100001
  • ‘0’ → 48 → 二进制 00110000
  • 空格 → 32 → 二进制 00100000

可以发现一个规律:同一个字母的大小写码位差32,这是一个很有用的特性,很多编程技巧都利用了这个特性。

ASCII的局限

ASCII最大的问题是只能表示英文字符,无法处理其他国家的语言。随着计算机在全球普及,这个问题越来越突出。

扩展ASCII编码

为了支持更多的字符,人们在ASCII的基础上进行了扩展,利用8位字节的最高位(原来ASCII只用到了低7位),这样就能表示256个字符。

ISO-8859系列编码

国际标准化组织(ISO)制定了一系列扩展ASCII编码标准,统称为ISO-8859系列:

  • ISO-8859-1 (Latin-1):支持西欧语言
  • ISO-8859-2 (Latin-2):支持中欧语言
  • ISO-8859-3 (Latin-3):支持南欧语言
  • ISO-8859-4 (Latin-4):支持北欧语言
  • … 等等,共有15个标准

这些编码的低128位和ASCII完全兼容,高128位用于表示各个地区的特殊字符。

问题

ISO-8859系列解决了欧洲语言的编码问题,但还是无法支持像中文、日文这样字符数量巨大的亚洲语言,因为8位最多只能表示256个字符,远远不够。

中文编码标准

中文有上万个常用字符,8位字节完全不够用,所以中文编码使用了多字节编码方案,一个中文字符用2个或更多字节表示。

GB2312编码

GB2312是我国1980年发布的第一个中文编码标准:

  • 全称《信息交换用汉字编码字符集·基本集》
  • 共收录6763个常用汉字和682个非汉字符号
  • 每个汉字用2个字节表示
  • 低128位仍然和ASCII兼容,是对ASCII的中文扩展
  • 兼容ASCII的部分称为半角字符,中文部分称为全角字符

编码规则:

  • 第一个字节(高字节)范围:0xA1-0xF7
  • 第二个字节(低字节)范围:0xA1-0xFE
  • 两个字节的最高位都是1,和ASCII区分开

GBK编码

GB2312收录的汉字只有6763个,很多生僻字无法表示,所以1995年发布了GBK编码(汉字内码扩展规范):

  • 共收录21003个汉字,包含了所有GB2312的汉字,同时增加了繁体汉字和更多生僻字
  • 仍然是双字节编码
  • 完全向下兼容GB2312
  • 是Windows系统中文版本的默认编码

GB18030编码

为了支持更多的少数民族文字和 Unicode 字符,我国在2000年发布了GB18030编码:

  • 采用1、2、4字节混合编码
  • 收录了70244个汉字,包含了所有Unicode中的中文字符
  • 完全向下兼容GB2312和GBK
  • 是我国的国家强制标准

Big5编码

Big5是台湾、香港等地区使用的繁体中文编码标准:

  • 双字节编码
  • 收录13053个繁体汉字
  • 和GB系列编码不兼容,同样的字节序列在GBK和Big5中表示不同的汉字,这就是为什么繁体网站如果编码不指定会出现乱码的原因。

多字节编码的问题

这些各个国家自己制定的扩展编码虽然解决了本国语言的表示问题,但带来了很多新的问题:

1. 不兼容问题

同一个二进制序列在不同的编码标准中可能代表完全不同的字符。例如:

  • 字节序列 0xB0 0xA1 在GBK编码中表示汉字“啊“
  • 在Big5编码中表示汉字“屴“
  • 在ISO-8859-1编码中表示两个特殊符号

这就导致如果没有指定编码,文本就无法正确解码,跨语言交流非常容易出现乱码。

2. 字符集有限

每个编码只能表示特定语言的字符,无法同时包含多种语言。例如一篇同时包含中文、日文、韩文的文档,就无法用单一的GBK或Shift_JIS编码来表示。

3. 处理复杂

多字节编码是变长的,有些字符是1字节,有些是2字节,处理字符串的时候很容易出问题:

  • 计算字符串长度时需要区分单字节和多字节字符
  • 截取字符串时很容易把一个多字节字符从中间截断,导致乱码
  • 遍历字符串的复杂度比单字节编码高很多

正是因为这些问题,人们迫切需要一个全球统一的编码标准,这就催生了Unicode。

实践小技巧

在Windows系统中,如果遇到中文乱码,首先可以考虑是不是编码问题:

  • 中国大陆的文件默认编码通常是GBK
  • 台湾香港的文件默认编码通常是Big5
  • 程序输出乱码通常是因为控制台编码和程序输出编码不一致

思考问题

  1. 为什么中文编码需要用多字节?单字节最多能表示多少个字符?
  2. GB2312、GBK、GB18030之间的关系是什么?
  3. 你遇到过GBK和UTF-8之间转换导致的乱码问题吗?当时是怎么解决的?