返回博客

07. 字符编码:从 ASCII 到 Unicode

07. 字符编码:从 ASCII 到 Unicode

第七篇:字符编码——从 ASCII 到 Unicode

前言

你是否遇到过乱码问题?中文网页显示为 中国、文件名变成问号,或者爬虫爬回来的数据打不开?这些问题的根源都是字符编码。本文彻底讲解 Python 中的字符编码体系:ASCII、GB2312、Unicode、UTF-8 的发展历程与区别,Python 3 中字符串的编码方式,以及 encode() 和 decode() 的正确用法。

什么是字符编码?

计算机只能存储和处理数字(0 和 1)。为了让计算机能够表示字母、汉字等字符,人类发明了编码表——为每个字符分配一个唯一的数字编号。

字符 'A' → 编码表查找 → 数字 65 → 二进制 01000001 → 存入内存
字符 '中' → 编码表查找 → 数字 20013 → 二进制 ... → 存入内存

不同的编码标准有不同的规则,混用编码就会乱码。

ASCII 编码:一切的起点

ASCII(American Standard Code for Information Interchange)是最早的字符编码标准,发布于 1963 年。

项目 说明
位数 7 位(实际占 1 字节)
字符数 128 个(0 ~ 127)
覆盖范围 英文大小写字母、数字 0-9、常用标点、控制字符

ASCII 编码表片段

十进制 十六进制 字符 说明
48 0x30 0 数字零
65 0x41 A 大写 A
97 0x61 a 小写 a
10 0x0A \n 换行符
32 0x20 空格

Python 中使用 ASCII

# ord():获取字符的 ASCII 码值
print(ord('A'))     # 65
print(ord('a'))     # 97
print(ord('0'))     # 48

# chr():将 ASCII 码值转回字符
print(chr(65))      # 'A'
print(chr(97))      # 'a'

ASCII 的局限很明显:只覆盖了英语字符,无法表示中文。

GB2312 与 GBK:中文编码方案

GB2312 是中国第一个官方汉字编码标准,发布于 1980 年。

编码标准 年份 收录字符
GB2312 1980 ~6700 汉字
GBK 1.0 1995 ~21000 汉字
GB18030 2000 ~70000 汉字
# 使用 GBK 编码读写文件
with open("chinese.txt", "r", encoding="gbk") as f:
    content = f.read()

# 将字符串编码为 GBK 格式的字节
text = "你好,中国!"
gbk_bytes = text.encode("gbk")
print(gbk_bytes)  # b'\xc4\xe3\xba\xc3\xa3\xac\xd6\xd0\xb9\xfa\xa3\xa1'

# 从 GBK 字节解码为字符串
decoded = gbk_bytes.decode("gbk")
print(decoded)  # 你好,中国!

Unicode:统一字符的标准

Unicode 的核心理念:给世界上每个字符分配一个唯一的编号(码点)。

项目 说明
起始版本 Unicode 1.0(1991 年)
当前版本 Unicode 15.0+
码点数 已分配超过 149,000 个字符
# Unicode 码点格式:U+XXXX
print(hex(ord('A')))      # 0x41 — U+0041
print(hex(ord('中')))     # 0x4e2d — U+4E2D
print(hex(ord('😀')))     # 0x1f600 — U+1F600

print(chr(0x4e2d))        # '中'
print(chr(0x1f600))       # '😀'

BMP(基本多文种平面)U+0000~U+FFFF 包含几乎所有常见文字。

UTF-8:Unicode 的终极实现

Unicode 定义编号,UTF-8 是实际存入文件或网络的编码格式。

为什么选择 UTF-8?

编码 字节特点 适用场景
ASCII 1 字节 仅英文
UTF-8 1~4 字节 互联网首选 ✅
UTF-16 2~4 字节 Java 内部存储
UTF-32 4 字节 固定宽度场景

UTF-8 的优势:

  1. 完全兼容 ASCII — ASCII 字符仍占 1 字节
  2. 自同步 — 即使读取中间位置也能正确定位字符边界
  3. 无字节序问题 — 不需要 BOM

UTF-8 编码规则

字符范围 UTF-8 字节数 字节模式
U+0000 ~ U+007F 1 字节 0xxxxxxx
U+0080 ~ U+07FF 2 字节 110xxxxx 10xxxxxx
U+0800 ~ U+FFFF 3 字节 1110xxxx 10xxxxxx 10xxxxxx
U+10000+ 4 字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

具体编码示例

# ASCII 字符 — 1 字节
print(len("A".encode("utf-8")))     # 1

# 中文(属于 BMP,占 3 字节)
print(len("中".encode("utf-8")))    # 3
# U+4E2D → E4 B8 AD

# 表情符号(占 4 字节)
print(len("😀".encode("utf-8")))    # 4
# U+1F600 → F0 9F 98 80

print("中".encode("utf-8").hex())   # e4b8ad

Python 3 中的字符串与编码

字符串的本质

Python 3 的所有字符串都是 Unicode 对象:

name = "小明"
print(type(name))            # <class 'str'>
print(len(name))             # 2 — 两个字符
print([ord(c) for c in name])  # [23567, 26126] — Unicode 码点

encode() 与 decode()

这是处理编码的核心方法:

# encode(): str → bytes(编码:字符串 → 字节序列)
text = "你好"
utf8_bytes = text.encode("utf-8")
print(utf8_bytes)           # b'\xe4\xbd\xa0\xe5\xa5\xbd'
print(type(utf8_bytes))     # <class 'bytes'>

gbk_bytes = text.encode("gbk")
print(len(utf8_bytes))      # 6 — UTF-8 占 6 字节
print(len(gbk_bytes))       # 4 — GBK 占 4 字节

# decode(): bytes → str(解码:字节序列 → 字符串)
decoded_utf8 = utf8_bytes.decode("utf-8")
print(decoded_utf8)         # "你好"

# ⚠️ 编码和解码必须匹配
wrong_decode = utf8_bytes.decode("gbk", errors="replace")
print(wrong_decode)         # 乱码

文件的读写编码

# 写入文件时指定编码
with open("output.txt", "w", encoding="utf-8") as f:
    f.write("这是一段中文文本\n")

# 读取文件时指定编码
with open("output.txt", "r", encoding="utf-8") as f:
    content = f.read()

BytesIO 与 StringIO

在内存中进行字节级操作:

from io import BytesIO, StringIO

bio = BytesIO()
bio.write("你好".encode("utf-8"))
print(bio.getvalue())  # b'\xe4\xbd\xa0\xe5\xa5\xbd'

bio.seek(0)
data = bio.read().decode("utf-8")
print(data)  # "你好"

常见编码问题排查指南

乱码排查步骤

# 尝试多种编码读取
filename = "data.txt"
for enc in ["utf-8", "gbk", "latin-1"]:
    try:
        with open(filename, "r", encoding=enc) as f:
            content = f.read()
        print(f"{enc}: 成功读取")
        break
    except UnicodeDecodeError:
        continue

Emoji 导致报错

某些数据库不支持 emoji(需要 4 字节的 UTF-8),确保使用 utf8mb4 编码。

URL 中的中文

import urllib.parse
name = "北京美食"
encoded = urllib.parse.quote(name)
print(encoded)  # %E5%8C%97%E4%BA%AC%E7%BE%8E%E9%A3%9F

BOM 前缀问题

with open("bom_file.txt", "r", encoding="utf-8-sig") as f:
    content = f.read()  # 自动跳过 BOM

编码速查表

编码 位数 兼容性 用途
ASCII 7 位 基础 早期英文
UTF-8 1~4 字节 兼容 ASCII 互联网首选
UTF-16 2~4 字节 不兼容 Java 内部
GBK 2 字节 兼容 GB2312 Windows 中文
Unicode — 抽象标准 编号系统

总结

本文讲解了字符编码的核心知识:

  1. ASCII — 128 个英文字符,编码表的起点
  2. GBK/GB2312 — 中文编码标准
  3. Unicode — 万国码编号系统
  4. UTF-8 — 最常用的实现,1~4 字节可变长
  5. encode()/decode() — Python 编码转换核心方法
  6. 乱码排查 — 检查编码、多编码尝试、注意 BOM

下一篇文章将深入讲解变量与常量:动态类型机制、赋值语句本质、引用模型。


本文是 Python 教程系列的第七篇。