# 字节编码
- 二进制编码(Binary Encoding):指将一个字符转换成二进制编码值,即从 string 类型转换成 bytes 类型。
- 二进制编码值通常以字节为单位存储,比如长度为 1 字节、2 字节,因此又称为字节编码。
- 例如字母 'A' 按 ASCII 编码之后,会转换成二进制值
0100 0001
。
# 相关概念
- 字节:在计算机中,指一个 8 bits 大小的二进制数据。
- 字符:在计算机中,指一个自然语言的文字,比如字母 'A' ,通常用 char 数据类型的变量存储,比如一个字母 'A' 通常占 1 字节的存储空间。
- 字符串:在计算机中,指一连串的多个字符,通常用 string 数据类型的变量存储。
- 文本:在现实世界中,指由一些自然语言的文字组成的信息,比如一句话。这些信息传入计算机中时,视作一条字符串,通常用 string 数据类型的变量存储。
- 计算机程序将文本写入文件中存储时,通常要转换成二进制编码的数据。例如:
- 程序将一个字符串 "Hello World!" 按 ASCII 格式编码,转换成二进制数据,写入文件。
- 程序从文件读取二进制数据,按 ASCII 格式解码,转换成一个字符串 "Hello World!" 。
- 如果解码的格式与编码格式不一致,得到的字符串就可能与原字符串不一致,比如出现乱码。
- 如果一个字符的编码值超过一个字节,则称为宽字符。
# 字节序
- 如果一个数据类型的长度超过 1 个字节,则需要考虑字节序(Byte Order)的问题:
- 大端序(Big-Endian):高位字节先存储。例如
0x11223344
存储为0x11 0x22 0x33 0x44
,最高位的字节 0x11 存储在地址的最低位。 - 小端序(Little-Endian) :低位字节先存储。例如
0x11223344
存储为0x44 0x33 0x22 0x11
,最高位的字节 0x11 存储在地址的最高位。
- 大端序(Big-Endian):高位字节先存储。例如
- 如何判断一段文本的字节序?
- 可以为每种字节序定义一种编码格式。例如将 UTF-16 编码格式细分为 UTF-16be 和 UTF-16le 。
- 可以在一个文本文件的开头加上几个字节作为标记,用于声明字节序,称为 BOM(Byte Order Mark)。
# 单字节编码
# ASCII
- :美国信息交换标准代码(American Standard Code for Information Interchange),一种简单、基础的单字节编码格式。
- 编码范围为 0x00 ~ 0x7F ,总共定义了 128 个字符。
- 收录了拉丁字母、一些控制字符。
# ISO-8859-1
- :又称为 Latin-1 ,是一种单字节编码格式,编码范围为 0x00 ~ 0xFF 。
- 是 ASCII 码的超集。
- 收录了拉丁字母、希腊语、阿拉伯语等字符。
- 用到了单字节的所有编码。
- 因此,任意一个 bytes 对象,不管它存储了什么二进制内容,即使存储了一个图片文件,都可以按照 ISO-8859-1 格式解码成 str 对象,虽然得到的 str 字符串不一定有意义:
>>> b'\xFF'.decode('ISO-8859-1') 'ÿ' >>> '你'.encode('gbk').decode('ISO-8859-1') 'Äã'
- 因此,任意一个 bytes 对象,不管它存储了什么二进制内容,即使存储了一个图片文件,都可以按照 ISO-8859-1 格式解码成 str 对象,虽然得到的 str 字符串不一定有意义:
# Windows 1252
- :又称为 cp1252 、ANSI 字符集,是 ISO-8859-1 的超集。
# 多字节编码
# Unicode
- :一种字符集,又称为万国码、国际码、统一码,收录了世界上很多种语言的字符,给每个字符分配了唯一编码。
- 编码范围为 U+000000 ~ U+10FFFF ,总共大约有 1.1 百万个码位(Code Point),可用于映射字符。
- 开头两个字节从 U+00 到 U+10 划分为 17 个平面(plane),每个平面包含 2^16=65536 个码位。
- 第一个平面称为第零平面、基本多语言平面(Basic Multilingual Plane, BMP),其它平面称为辅助平面。
- 第零平面包含了最常用的字符,其中 D800 ~ DFFF 范围内的码位保留,不映射 Unicode 字符,因此通常被 UTF 编码使用。
- 每个字符编码为 6 个十六进制数,加上
U+
或\u
作为前缀。- 只考虑第零平面时,可以只使用 4 个十六进制数,比如字母 A 表示成 U+0041 。
# UTF
- :Unicode 转换格式(Unicode Transformation Format)
- Unicode 字符的编码较长,大部分码位都用不到,浪费存储空间。因此实际使用时,通常通过 UTF 转换成更短的编码格式。
# UTF-8
- :一种编码格式,兼容 ASCII 码。
- 字符的编码长度可变,为 1 ~ 4 个字节。比如存储 ASCII 码字符时只用 1 个字节,存储中文字符时使用 3 个字节。
- UTF-8 的编码长度可变,因此不存在多种字节序。
- 按照 UTF-8 规范,处理 UTF-8 编码的文本时,不需要考虑字节序。
- 不过,Microsoft 公司的 Office 等软件习惯了在处理 UTF-8 编码的文本时,加上
0xEF 0xBB 0xBF
三个字节作为 BOM 标记,因此与标准的 UTF-8 格式不同,这会导致一些问题:- 例如,用 VS Code 软件编写一个 UTF-8 编码、包含中文字符的 csv 表格文件之后,用 Excel 软件打开该文件,显示的字符会乱码。此时可用 Notepad++ 软件将该文件转换成 UTF-8-BOM 编码格式。或者用
iconv 1.csv -f utf-8 -t gbk > 2.csv
命令将该文件转换成 gbk 编码格式,则不需要声明字节序。 - 反之,用 Excel 导出一个包含中文字符的 csv 表格文件之后,用 VS Code 打开该文件,显示的字符会乱码。此时可用 Notepad++ 软件将该文件转换成 UTF-8 编码格式。
- 例如,用 VS Code 软件编写一个 UTF-8 编码、包含中文字符的 csv 表格文件之后,用 Excel 软件打开该文件,显示的字符会乱码。此时可用 Notepad++ 软件将该文件转换成 UTF-8-BOM 编码格式。或者用
# UTF-16
- :一种编码格式,不兼容 ASCII 码。
- 大部分字符的编码长度是 2 字节,少部分不常用的字符则是 4 字节。
- 根据字节序的不同,细分为两种编码格式:
- UTF-16be :大端序,BOM 标记为
0xFE 0xFF
。 - UTF-16le :小端序,BOM 标记为
0xFF 0xFE
。
- UTF-16be :大端序,BOM 标记为
- 例:
>>> '12'.encode('utf-16') # 编码格式名称中没有声明字节序,Python 默认会按小端序处理 b'\xff\xfe1\x002\x00' # 编码之后,会在开头加上 BOM 标记,末尾加上空字符 \x00 表示结束 >>> 'A'.encode('utf-16') b'\xff\xfeA\x00' >>> ''.encode('utf-16be') # 如果编码格式名称中声明了字节序,则不必再加上 BOM 标记 b'' >>> 'A'.encode('utf-16le') b'A\x00'
# UTF-32
- :一种编码格式,不兼容 ASCII 码。
- 每个字符的编码长度都是 4 字节。
- 根据字节序的不同,细分为两种编码格式:
- UTF-32be :大端序,BOM 标记为
0x00 0x00 0xFE 0xFF
。 - UTF-32le :小端序,BOM 标记为
0xFF 0xFE 0x00 0x00
。
- UTF-32be :大端序,BOM 标记为
# UCS
UCS
- :通用编码字符集(Universal Coded Character Set),由 ISO 10646 标准定义,相当于 Unicode 字符集的子集。
UCS-2
- :一种编码格式,是 UTF-16 的子集。
- 只对 Unicode 第零平面的字符进行编码。
- 每个字符的编码长度都是 2 字节。
- 根据字节序的不同,细分为两种编码格式:
- UCS-2be
- UCS-2le
UCS-4
- :一种编码格式,是 UTF-32 的超集。
- 每个字符的编码长度都是 4 字节。
# 中文编码
# GB2312
- :一种简体中文的编码格式,于 1980 年发布,已经过时。
- 在 Windows 中别名为 CP936 。
- 收录了 6k 多个汉字、拉丁字母等。
# BIG5
- :一种繁体中文的编码格式,又称为大五码,于 1983 年发布。
- 收录了 1w 多汉字、拉丁字母等。
# GBK
- :一种中文编码格式,于 1995 年发布。
- 向下兼容 GB2312 ,向上支持 ISO 10646 标准,也收录了 BIG5 中的繁体汉字。总共收录了 2w 多个汉字。
- 英文字符的编码长度为 1 字节,中文字符的编码长度为 2 字节。
# GB18030
- :一种中文编码格式,于 2000 年发布。
- 是 GBK 的超集。总共收录了 7w 多个汉字。
- 字符的编码长度可变,包括:
- 1 字节,向下兼容 ASCII 。
- 2 字节,向下兼容 GBK 。
- 4 字节
# import codecs
:Python 的标准库,用于管理 Python 的编解码器。
- 它将编解码器称为 codec ,包括 encoder 和 decoder 。
- 它为所有编解码器定义了抽象的基类:
class Codec: def encode(self, input, errors='strict'): pass def decode(self, input, errors='strict'): pass
- Python 解释器内置了很多种编码格式的编解码器,它们都在 codecs 模块中注册了。
- 编码格式的名称不区分大小写。对于连字符 - ,可以替换成下划线 _ ,也可以省略。
- 例:查询一种编码格式对应的编解码器
>>> import codecs >>> codecs.lookup('utf-8') <codecs.CodecInfo object for encoding utf-8 at 0x151dc6a99a0> >>> codecs.lookup('utf_8') <codecs.CodecInfo object for encoding utf-8 at 0x151dcf59400> >>> codecs.lookup('utf8') <codecs.CodecInfo object for encoding utf-8 at 0x151dcf59280>
# import chardet
:Python 的第三方库,用于检测 bytes 对象采用的字节编码格式。
- 安装:
pip install chardet
- 其原理是通过比对各种字符集的特征字符来猜测编码格式。
- 检测的数据量太少就容易猜错。
- 不能处理 str 对象,因为 str 已经是按 Unicode 格式编码了。
- 例:检测一个字符串
>>> import chardet >>> chardet.detect(b'Hello, world!') {'encoding': 'ascii', 'confidence': 1.0, 'language': ''} # confidence 表示可信度,这里为 100% >>> chardet.detect('你'.encode('GBK'))) {'encoding': None, 'confidence': 0.0, 'language': None} >>> chardet.detect('你好'.encode('GBK')) {'encoding': 'TIS-620', 'confidence': 0.3598212120361634, 'language': 'Thai'} >>> chardet.detect('你好,欢迎来到这里!'.encode('GBK')) # 增加检测的字符数量时,检测结果可能改变 {'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}
- 即使 chardet 将一个字符串识别为 GB2312 格式,建议也当作 GBK 格式解码。以免 chardet 将 GBK 格式的字符串,误识别成了 GB2312 格式。
- 例:检测一个文件
>>> f = open(path, 'rb') >>> chardet.detect(f.read())['encoding'] 'GB2312'