字符编码

五十岚2020年5月2日
大约 4 分钟

是否重要取决于编程场景,对只处理 ASCII 文本的程序员没有影响

字符编码

Python3 明确区分了人类可读的 文本字符串 和原始的 字节序列

编解码器 文档open in new window

1. 字符

1.1 编码中的字符串

字符串: 字符组成的序列,问题出现在 “字符” 的定义上

字符: 2015 年将字符最佳定义为 Unicode 字符,因此 Python3str 对象获取的元素就是 Unicode 字符

Unicode 标准把 字符的标识 和具体的 字节表述 进行了如下区分

  • 字符的标识:即 码位 ,是 0 ~ 1 114 111 的数字 (十进制),在 Unicode 标准中以 4 ~ 6 个十六进制数字表示,而且加前缀 U+
    • A 的码位 为 U+0041
  • 字节表述:取决于所用的编码,在 UTF-8 编码中,A (U+0041) 的码位编码为单字节 \x41,而在 UTF-16LE 编码中编码成两个字节 \x41\x00
  • 编码:在 码位字节序列 之间转换时使用的算法

提示

码位字符串) 转换成 字节序列字节串) 的过程是编码(encode),把 字节序列 转化为 码位 的过程是解码(decode)

s = "coffeé"
print(len(s))

b_sequence = s.encode("utf8")
print(b_sequence, len(b_sequence))

print(b_sequence.decode("utf8"))

### 输出结果
# 6
# b'coffe\xc3\xa9' 7
# coffeé
  • 字符串 coffeé6Unicode 字节
  • s.encode("utf8") 使用 UTF-8str 对象 编码为 bytes 对象,该对象以 b 开头
  • 字节序列 b_sequence7 个字节(在 UTF-8é 的码位编码成 2 个字节)
  • 使用 bytes.decode("utf-8")bytes 对象解码为 str 对象

1.2 Python 编码发展史

1.2.1 ASCII 码

8 位,即 一个字节 表示字符 0 ~ 127 已编好的 英文拉丁字符

提示

后为了扩容,各国根据索引等方式又进行进一步的编码

中国:

  • gb2312: 只收录了约 6700 多个中文 (1980 年)
  • gbk1.0: 收录了 2 万 多个字符 (1995 年)
  • gb18030:27000 左右中文 (2000 年)
1.2.2 Unicode 万国码

Unicode: 兼容各国的编码,是一种国际通用标准, utf-8 则是 Unicode 的子集

  • utf-32: 即一个字符占 324 字节

  • utf-16: 一个字符占 2 字节 及以上,65535

  • utf-8: Unicode 的子集,英文用 ASCII 码 来存储

    亚洲国家,如: 中 / 日3 个字节

1.2.3 Python 的编码

Python2 的默认编码为 ASCII 码,字符串也都是字节,故文件头通常声明编码方式,如

#-- coding:utf-8 --
  • Python2 要声明 coding:utf-8,告诉用 ASCII 编码解释器是 utf-8 编码的文件
    • 解释器: 打开文件,将一行行代码放到内存解释
  • Python2 存储的也是 utf-8 编码的文件
  • 解释器 和 磁盘存盘的编码一致 才能顺利执行,不会乱码

Python3 则不同于 2,默认是 Unicode 万国码,u''省略)表示,故可以在 gbk 编码下正常显示中文

import sys

# 检测系统编码
sys.getdefaultencoding()
## 'utf-8'

str_a = u"中文字符串"
str_a.encode("utf-8")
## b'\xe4\xb8\xad\xe6\x96\x87\xe5\xad\x97\xe7\xac\xa6\xe4\xb8\xb2'

bytes_b = str_a.encode("gbk")
## b'\xd6\xd0\xce\xc4\xd7\xd6\xb7\xfb\xb4\xae'

bytes_b.decode("gbk")
## '中文字符串'
  • str_a 定义了一串中文字符串,不同编码形式的下的字节串不同
  • b''bytes 字节,为 16 进制 [0-255] 的数字,每个国家都有自己的 bytes
  • str_a.encode("gbk") 返回了按 gbk 编码后, bytes 类型的字节串码(密文
  • bytes_b.decode("gbk") 按照 gbk 编码,又解码回为原来的 Unicode 字符串

编码、转码通常在 数据传输交互 等使用

若需要查看中文字符的万国码,可使用

s = "你好hello"

json.dumps(s)
## '"\\u4f60\\u597dhello"'

Python3.py 存储的文件,的确是 utf-8 编码,但内存生成时会变为 Unicode 编码,这是为了符合 ISO 统一标准,传给 解释器 执行


2. 常见问题

2.1 操作系统编码

查看 Linux 操作系统语言环境变量

$ echo $LANG
# zh_CN.UTF-8 或 en_US.UTF-8 之类的

当出现中文时,可能会导致引用的库,执行命令异常,如 nmcli

# 源码: nmcli._syscmd = SystemCommand().nmcli
# 默认 'LAN' 为 C

r = self._run(commands, capture_output=True, check=True, env={'LANG': 'zh_CN.UTF-8'})
  • 此时需对源码进行继承和改写

  • 或若允许,可直接设置环境变量为 C.UTF-8

    $ export LANG=C.UTF-8
    
上次编辑于: 2022/9/20 06:19:59