作者: andylin02
学习章节: 第 4 章 文本和字节序列
关键词: Unicode|UTF-8|编码|解码|bytes|bytearray|memoryview|struct|UnicodeEncodeError|UnicodeDecodeError|Unicode三明治|unicodedata|normalize|casefold|locale.strxfrm|BOM|chardet
一、本章概述
人类使用文本,计算机使用字节序列。 —— Esther Nam 和 Travis Fischer
Python 3 明确区分了人类可读的文本字符串和原始的字节序列。隐式地把字节序列转换成 Unicode 文本已经成为过去。
本章的核心内容围绕三类问题展开:
- Unicode 字符串:字符的标识(码位)及其具体表示的差异
- 二进制序列:bytes 与 bytearray 的语义和使用
- 编解码:在文本字符串与字节序列之间进行可靠、无歧义的转换
本章内容大纲
- 字符、码位和字节表示的基本概念
- bytes 和 bytearray 的特性
- struct 处理打包字节和 memoryview 内存视图
- 基本的编解码器及其用法
- 编解码错误(UnicodeEncodeError、UnicodeDecodeError)处理
- 处理文本文件的最佳实践:「Unicode 三明治」
- Unicode 规范化与安全比较
- Unicode 文本排序
- 支持字符串和字节序列的双模式 API
本章核心概念架构图
#mermaid-svg-iby3pTjKtjEmQtCN{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-iby3pTjKtjEmQtCN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iby3pTjKtjEmQtCN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iby3pTjKtjEmQtCN .error-icon{fill:#552222;}#mermaid-svg-iby3pTjKtjEmQtCN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iby3pTjKtjEmQtCN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iby3pTjKtjEmQtCN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iby3pTjKtjEmQtCN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iby3pTjKtjEmQtCN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iby3pTjKtjEmQtCN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iby3pTjKtjEmQtCN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iby3pTjKtjEmQtCN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iby3pTjKtjEmQtCN .marker.cross{stroke:#333333;}#mermaid-svg-iby3pTjKtjEmQtCN svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iby3pTjKtjEmQtCN p{margin:0;}#mermaid-svg-iby3pTjKtjEmQtCN .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-iby3pTjKtjEmQtCN .cluster-label text{fill:#333;}#mermaid-svg-iby3pTjKtjEmQtCN .cluster-label span{color:#333;}#mermaid-svg-iby3pTjKtjEmQtCN .cluster-label span p{background-color:transparent;}#mermaid-svg-iby3pTjKtjEmQtCN .label text,#mermaid-svg-iby3pTjKtjEmQtCN span{fill:#333;color:#333;}#mermaid-svg-iby3pTjKtjEmQtCN .node rect,#mermaid-svg-iby3pTjKtjEmQtCN .node circle,#mermaid-svg-iby3pTjKtjEmQtCN .node ellipse,#mermaid-svg-iby3pTjKtjEmQtCN .node polygon,#mermaid-svg-iby3pTjKtjEmQtCN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-iby3pTjKtjEmQtCN .rough-node .label text,#mermaid-svg-iby3pTjKtjEmQtCN .node .label text,#mermaid-svg-iby3pTjKtjEmQtCN .image-shape .label,#mermaid-svg-iby3pTjKtjEmQtCN .icon-shape .label{text-anchor:middle;}#mermaid-svg-iby3pTjKtjEmQtCN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-iby3pTjKtjEmQtCN .rough-node .label,#mermaid-svg-iby3pTjKtjEmQtCN .node .label,#mermaid-svg-iby3pTjKtjEmQtCN .image-shape .label,#mermaid-svg-iby3pTjKtjEmQtCN .icon-shape .label{text-align:center;}#mermaid-svg-iby3pTjKtjEmQtCN .node.clickable{cursor:pointer;}#mermaid-svg-iby3pTjKtjEmQtCN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-iby3pTjKtjEmQtCN .arrowheadPath{fill:#333333;}#mermaid-svg-iby3pTjKtjEmQtCN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-iby3pTjKtjEmQtCN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-iby3pTjKtjEmQtCN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iby3pTjKtjEmQtCN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-iby3pTjKtjEmQtCN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iby3pTjKtjEmQtCN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-iby3pTjKtjEmQtCN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-iby3pTjKtjEmQtCN .cluster text{fill:#333;}#mermaid-svg-iby3pTjKtjEmQtCN .cluster span{color:#333;}#mermaid-svg-iby3pTjKtjEmQtCN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-iby3pTjKtjEmQtCN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-iby3pTjKtjEmQtCN rect.text{fill:none;stroke-width:0;}#mermaid-svg-iby3pTjKtjEmQtCN .icon-shape,#mermaid-svg-iby3pTjKtjEmQtCN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iby3pTjKtjEmQtCN .icon-shape p,#mermaid-svg-iby3pTjKtjEmQtCN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-iby3pTjKtjEmQtCN .icon-shape .label rect,#mermaid-svg-iby3pTjKtjEmQtCN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iby3pTjKtjEmQtCN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-iby3pTjKtjEmQtCN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-iby3pTjKtjEmQtCN :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
编解码转换
计算机视角
人类视角
编码
解码
字符串 (str)Unicode 文本
字节序列 (bytes/bytearray)原始字节数据
编码 (encode)str → bytes
解码 (decode)bytes → str
UnicodeEncodeError目标编码不支持字符
UnicodeDecodeError字节序列对不上解码器
二、字符与编码基础
在 Unicode 标准中,每个字符都有两个层面的定义:
在 Python 3 中,str 类型相当于 Python 2 中的 unicode 类型,内存中以码位对应二进制序列存储,而没有进行编码。
把码位转换成字节序列的过程是编码(encoding);把字节序列转换成码位的过程是解码(decoding)。
基本的编码和解码操作如下:
# 编码(encoding):从字符串到字节序列
s = 'café'
print(f'字符串长度(字符数): {len(s)}') # 4 个字符
# 使用 UTF-8 编码
b = s.encode('utf-8')
print(f'编码后的字节序列: {b}') # b'caf\\xc3\\xa9'
print(f'字节序列长度: {len(b)}') # 5 个字节('é' 在 UTF-8 中占用 2 个字节)
# 解码(decoding):从字节序列到字符串
c = b.decode('utf-8')
print(f'解码后的字符串: {c}') # 'café'
编码/解码流程图
#mermaid-svg-McDGxn255AX7mJ3k{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-McDGxn255AX7mJ3k .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-McDGxn255AX7mJ3k .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-McDGxn255AX7mJ3k .error-icon{fill:#552222;}#mermaid-svg-McDGxn255AX7mJ3k .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-McDGxn255AX7mJ3k .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-McDGxn255AX7mJ3k .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-McDGxn255AX7mJ3k .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-McDGxn255AX7mJ3k .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-McDGxn255AX7mJ3k .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-McDGxn255AX7mJ3k .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-McDGxn255AX7mJ3k .marker{fill:#333333;stroke:#333333;}#mermaid-svg-McDGxn255AX7mJ3k .marker.cross{stroke:#333333;}#mermaid-svg-McDGxn255AX7mJ3k svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-McDGxn255AX7mJ3k p{margin:0;}#mermaid-svg-McDGxn255AX7mJ3k .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-McDGxn255AX7mJ3k .cluster-label text{fill:#333;}#mermaid-svg-McDGxn255AX7mJ3k .cluster-label span{color:#333;}#mermaid-svg-McDGxn255AX7mJ3k .cluster-label span p{background-color:transparent;}#mermaid-svg-McDGxn255AX7mJ3k .label text,#mermaid-svg-McDGxn255AX7mJ3k span{fill:#333;color:#333;}#mermaid-svg-McDGxn255AX7mJ3k .node rect,#mermaid-svg-McDGxn255AX7mJ3k .node circle,#mermaid-svg-McDGxn255AX7mJ3k .node ellipse,#mermaid-svg-McDGxn255AX7mJ3k .node polygon,#mermaid-svg-McDGxn255AX7mJ3k .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-McDGxn255AX7mJ3k .rough-node .label text,#mermaid-svg-McDGxn255AX7mJ3k .node .label text,#mermaid-svg-McDGxn255AX7mJ3k .image-shape .label,#mermaid-svg-McDGxn255AX7mJ3k .icon-shape .label{text-anchor:middle;}#mermaid-svg-McDGxn255AX7mJ3k .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-McDGxn255AX7mJ3k .rough-node .label,#mermaid-svg-McDGxn255AX7mJ3k .node .label,#mermaid-svg-McDGxn255AX7mJ3k .image-shape .label,#mermaid-svg-McDGxn255AX7mJ3k .icon-shape .label{text-align:center;}#mermaid-svg-McDGxn255AX7mJ3k .node.clickable{cursor:pointer;}#mermaid-svg-McDGxn255AX7mJ3k .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-McDGxn255AX7mJ3k .arrowheadPath{fill:#333333;}#mermaid-svg-McDGxn255AX7mJ3k .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-McDGxn255AX7mJ3k .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-McDGxn255AX7mJ3k .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-McDGxn255AX7mJ3k .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-McDGxn255AX7mJ3k .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-McDGxn255AX7mJ3k .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-McDGxn255AX7mJ3k .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-McDGxn255AX7mJ3k .cluster text{fill:#333;}#mermaid-svg-McDGxn255AX7mJ3k .cluster span{color:#333;}#mermaid-svg-McDGxn255AX7mJ3k div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-McDGxn255AX7mJ3k .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-McDGxn255AX7mJ3k rect.text{fill:none;stroke-width:0;}#mermaid-svg-McDGxn255AX7mJ3k .icon-shape,#mermaid-svg-McDGxn255AX7mJ3k .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-McDGxn255AX7mJ3k .icon-shape p,#mermaid-svg-McDGxn255AX7mJ3k .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-McDGxn255AX7mJ3k .icon-shape .label rect,#mermaid-svg-McDGxn255AX7mJ3k .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-McDGxn255AX7mJ3k .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-McDGxn255AX7mJ3k .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-McDGxn255AX7mJ3k :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
内存中的表示
存储/传输
bytes 对象原始字节数据可在网络/磁盘间传递
解码过程
b.decode('utf-8')
根据编码规则将字节还原为码位序列
结果: 'café'
编码过程
s.encode('utf-8')
根据编码规则将码位映射为字节序列
结果: b'caf\\xc3\\xa9'
str 对象'café'码位: U+0063, U+0061, U+0066, U+00E9
从上图可以看出,str 对象始终存在于内存中,bytes 对象则是存储或传输的形式。理解和记忆这「两条腿走路」的抽象,对于避免编解码错误至关重要。
三、二进制序列:bytes 与 bytearray
Python 内置了两种基本的二进制序列类型:不可变的 bytes 和可变的 bytearray。
bytes 或 bytearray 对象的各个元素是 0-255(含)之间的整数。这一点和字符串截然不同:字符串的元素是长度为 1 的字符串,而二进制序列的元素是整数。
# 创建 bytes 对象
cafe = bytes('café', encoding='utf_8')
print(cafe) # b'caf\\xc3\\xa9'
print(cafe[0]) # 99(c 的 ASCII 码)
print(cafe[:1]) # b'c'(切片仍然是 bytes 对象,哪怕是长度为 1 的切片)
# 创建 bytearray 对象(可变)
cafe_arr = bytearray(cafe)
print(cafe_arr) # bytearray(b'caf\\xc3\\xa9')
print(cafe_arr[–1:]) # bytearray(b'\\x89')
bytearray 没有字面量语法,显示时以 bytearray() 和字节序列字面量作为参数的形式呈现。
bytes 对象的显示规则
在打印或显示 bytes 对象时,Python 对不同的字节采用不同的显示方式:
- ASCII 范围内的字节:使用 ASCII 字符本身(如 ‘c’, ‘a’, ‘f’)
- 转义字符:制表符 \\t、换行符 \\n、回车符 \\r 和反斜杠 \\ 使用转义序列
- 其他字节的值:使用十六进制转义序列(如 \\xc3, \\xa9)
bytes vs bytearray 对比
#mermaid-svg-XRpjEPv5qcvKaIBc{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XRpjEPv5qcvKaIBc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XRpjEPv5qcvKaIBc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XRpjEPv5qcvKaIBc .error-icon{fill:#552222;}#mermaid-svg-XRpjEPv5qcvKaIBc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XRpjEPv5qcvKaIBc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XRpjEPv5qcvKaIBc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XRpjEPv5qcvKaIBc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XRpjEPv5qcvKaIBc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XRpjEPv5qcvKaIBc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XRpjEPv5qcvKaIBc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XRpjEPv5qcvKaIBc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XRpjEPv5qcvKaIBc .marker.cross{stroke:#333333;}#mermaid-svg-XRpjEPv5qcvKaIBc svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XRpjEPv5qcvKaIBc p{margin:0;}#mermaid-svg-XRpjEPv5qcvKaIBc .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-XRpjEPv5qcvKaIBc .cluster-label text{fill:#333;}#mermaid-svg-XRpjEPv5qcvKaIBc .cluster-label span{color:#333;}#mermaid-svg-XRpjEPv5qcvKaIBc .cluster-label span p{background-color:transparent;}#mermaid-svg-XRpjEPv5qcvKaIBc .label text,#mermaid-svg-XRpjEPv5qcvKaIBc span{fill:#333;color:#333;}#mermaid-svg-XRpjEPv5qcvKaIBc .node rect,#mermaid-svg-XRpjEPv5qcvKaIBc .node circle,#mermaid-svg-XRpjEPv5qcvKaIBc .node ellipse,#mermaid-svg-XRpjEPv5qcvKaIBc .node polygon,#mermaid-svg-XRpjEPv5qcvKaIBc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XRpjEPv5qcvKaIBc .rough-node .label text,#mermaid-svg-XRpjEPv5qcvKaIBc .node .label text,#mermaid-svg-XRpjEPv5qcvKaIBc .image-shape .label,#mermaid-svg-XRpjEPv5qcvKaIBc .icon-shape .label{text-anchor:middle;}#mermaid-svg-XRpjEPv5qcvKaIBc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XRpjEPv5qcvKaIBc .rough-node .label,#mermaid-svg-XRpjEPv5qcvKaIBc .node .label,#mermaid-svg-XRpjEPv5qcvKaIBc .image-shape .label,#mermaid-svg-XRpjEPv5qcvKaIBc .icon-shape .label{text-align:center;}#mermaid-svg-XRpjEPv5qcvKaIBc .node.clickable{cursor:pointer;}#mermaid-svg-XRpjEPv5qcvKaIBc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XRpjEPv5qcvKaIBc .arrowheadPath{fill:#333333;}#mermaid-svg-XRpjEPv5qcvKaIBc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XRpjEPv5qcvKaIBc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XRpjEPv5qcvKaIBc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XRpjEPv5qcvKaIBc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XRpjEPv5qcvKaIBc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XRpjEPv5qcvKaIBc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XRpjEPv5qcvKaIBc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XRpjEPv5qcvKaIBc .cluster text{fill:#333;}#mermaid-svg-XRpjEPv5qcvKaIBc .cluster span{color:#333;}#mermaid-svg-XRpjEPv5qcvKaIBc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XRpjEPv5qcvKaIBc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XRpjEPv5qcvKaIBc rect.text{fill:none;stroke-width:0;}#mermaid-svg-XRpjEPv5qcvKaIBc .icon-shape,#mermaid-svg-XRpjEPv5qcvKaIBc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XRpjEPv5qcvKaIBc .icon-shape p,#mermaid-svg-XRpjEPv5qcvKaIBc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XRpjEPv5qcvKaIBc .icon-shape .label rect,#mermaid-svg-XRpjEPv5qcvKaIBc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XRpjEPv5qcvKaIBc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XRpjEPv5qcvKaIBc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XRpjEPv5qcvKaIBc :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
bytes vs bytearray 对比
bytes────────不可变字面量语法: b'…'元素: 整数 0-255切片返回 bytes
bytearray────────可变无字面量语法元素: 整数 0-255切片返回 bytearray
四、更高级的二进制序列操作
4.1 memoryview:零复制访问内存
标准库里有一种内置类型 memoryview,它允许在不复制字节的情况下访问二进制数据,从而可以共享内存。memoryview 本身不能创建或存储字节序列,但它为其他二进制对象提供了共享内存访问视图,这在处理大型二进制数据时可以显著提升性能。
# 创建 memoryview 并修改底层数据
import array
# 创建一个数组
numbers = array.array('h', [–2, –1, 0, 1, 2])
print(f"原始数组: {numbers}")
# 创建内存视图
memv = memoryview(numbers)
print(f"内存视图长度: {len(memv)}")
print(f"内存视图内容: {memv.tolist()}") # [-2, -1, 0, 1, 2]
# 转换为无符号字节视图
memv_oct = memv.cast('B')
print(f"字节视图内容: {memv_oct.tolist()}")
# 通过内存视图修改数据(影响原始数据)
memv_oct[5] = 4 # 修改第 6 个字节为 4
print(f"修改后的数组: {numbers}")
4.2 struct:解析打包的二进制数据
struct 模块可以将打包的字节解析为不同类型字段的元组,也可以执行相反的操作。struct 与 bytes、bytearray 和 memoryview 对象配合使用,尤其适合处理二进制文件格式和网络协议。
import struct
import binascii
# 打包(pack):将 Python 值转换为字节序列
fmt = '<3s3sHH' # 小端字节序,两个3字节字符串,两个16位整数
packed = struct.pack(fmt, b'GIF', b'89a', 100, 200)
print(f"打包后的字节序列: {binascii.hexlify(packed)}")
# 解包(unpack):从字节序列提取字段值
unpacked = struct.unpack(fmt, packed)
print(f"解包后的元组: {unpacked}") # (b'GIF', b'89a', 100, 200)
# 更复杂的示例:使用 memoryview 与 struct 结合
data = b'\\x01\\x02\\x03\\x04\\x05\\x06'
fmt = '>HHB' # 大端序,两个无符号短整数,一个无符号字符
result = struct.unpack(fmt, data[:5])
print(f"部分数据解包: {result}")
五、基本编解码器
Python 发行版中包含了超过 100 种编解码器,用于在文本和字节之间相互转换。表中列出了一些最为常用的编解码器:
| utf-8 | utf8, U8 | 最常用,兼容 ASCII,能表示所有 Unicode 字符 |
| latin1 | iso8859_1 | 作为其他编码基础的编码,例如 cp1252 和 Unicode |
| cp1252 | windows-1252 | Windows 系统默认的西欧编码 |
| gb2312 | gbk | 编码简体中文的陈旧标准,亚洲语言中应用最广泛的多字节编码之一 |
| utf-16le | – | UTF-16 16 位编码方案的一种形式 |
下面的代码演示了基本编解码器的使用:
# 基本编码示例
content = 'São Paulo'
for codec in ['utf_8', 'utf_16']:
print(f"{codec}: {content.encode(codec)}")
# utf_8: b'S\\xc3\\xa3o Paulo'
# utf_16: b'\\xff\\xfeS\\x00\\xe3\\x00o\\x00 \\x00P\\x00a\\x00u\\x00l\\x00o\\x00'
关于编码器和别名:encode() 的 encoding 参数不仅接受包含下划线的官方名称 utf_8,也接受带连字符 utf-8 和全大写别名 U8。
六、处理编解码问题
6.1 异常类型
遇到编码/解码相关的问题时,首先要区分异常类型:
| UnicodeEncodeError | 将 str 转换为字节序列时 | 字符串中包含目标编码不支持的字符 |
| UnicodeDecodeError | 将字节序列读取为 str 时 | 字节序列不符合预期的编码格式 |
| SyntaxError | 在源码中使用非 ASCII 字符且未声明编码时 | Python 源文件默认编码为 UTF-8,但某些环境可能使用其他编码 |
6.2 处理 UnicodeEncodeError
content = 'São Paulo'
try:
# cp437 编码不支持 'ã' 字符
content.encode('cp437')
except UnicodeEncodeError as e:
print(f"编码错误: {e}")
# 'charmap' codec can't encode character '\\xe3' in position 1: character maps to <undefined>
# 错误处理策略
print(content.encode('cp437', errors='ignore')) # b'So Paulo'
print(content.encode('cp437', errors='replace')) # b'S?o Paulo'
print(content.encode('cp437', errors='xmlcharrefreplace')) # b'São Paulo'
errors 参数提供的几种常见策略:
- 'ignore':跳过无法编码的字符(会丢失信息)
- 'replace':用 ? 替换无法编码的字符
- 'xmlcharrefreplace':将无法编码的字符替换为 XML 实体(如 ã)
6.3 处理 UnicodeDecodeError
octets = b'Montr\\xe9al'
# 尝试不同的编码解码
print(octets.decode('cp1252')) # Montréal(正确)
print(octets.decode('iso8859_7')) # Montrιal(错误)
print(octets.decode('koi8_r')) # MontrИal(错误)
try:
print(octets.decode('utf-8'))
except UnicodeDecodeError as e:
print(f"解码错误: {e}") # 'utf-8' codec can't decode byte 0xe9 in position 5: invalid continuation byte
# 使用 replace 策略
print(octets.decode('utf-8', errors='replace')) # Montr�al
无法确定字节序列的编码时,可以使用第三方库 chardet 进行编码检测。chardet 能够检测 ASCII、GBK、UTF-8、日文等多种编码。
6.4 BOM(字节序标记)
UTF-16 编码的字节序列开头会有一个 \\ufffe 标记,用于表示字节序:
- 大端序(Big-endian):\\xfe\\xff
- 小端序(Little-endian):\\xff\\xfe
UTF-8 编码不需要 BOM,但微软在 UTF-8 文件中也加入了 BOM(\\xef\\xbb\\xbf)并因此而可能引发兼容性问题。
七、处理文本文件:Unicode 三明治
处理文本的最佳实践是「Unicode 三明治」:
#mermaid-svg-Oa1yRh73uc2RUc3u{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Oa1yRh73uc2RUc3u .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Oa1yRh73uc2RUc3u .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Oa1yRh73uc2RUc3u .error-icon{fill:#552222;}#mermaid-svg-Oa1yRh73uc2RUc3u .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Oa1yRh73uc2RUc3u .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Oa1yRh73uc2RUc3u .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Oa1yRh73uc2RUc3u .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Oa1yRh73uc2RUc3u .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Oa1yRh73uc2RUc3u .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Oa1yRh73uc2RUc3u .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Oa1yRh73uc2RUc3u .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Oa1yRh73uc2RUc3u .marker.cross{stroke:#333333;}#mermaid-svg-Oa1yRh73uc2RUc3u svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Oa1yRh73uc2RUc3u p{margin:0;}#mermaid-svg-Oa1yRh73uc2RUc3u .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-Oa1yRh73uc2RUc3u .cluster-label text{fill:#333;}#mermaid-svg-Oa1yRh73uc2RUc3u .cluster-label span{color:#333;}#mermaid-svg-Oa1yRh73uc2RUc3u .cluster-label span p{background-color:transparent;}#mermaid-svg-Oa1yRh73uc2RUc3u .label text,#mermaid-svg-Oa1yRh73uc2RUc3u span{fill:#333;color:#333;}#mermaid-svg-Oa1yRh73uc2RUc3u .node rect,#mermaid-svg-Oa1yRh73uc2RUc3u .node circle,#mermaid-svg-Oa1yRh73uc2RUc3u .node ellipse,#mermaid-svg-Oa1yRh73uc2RUc3u .node polygon,#mermaid-svg-Oa1yRh73uc2RUc3u .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Oa1yRh73uc2RUc3u .rough-node .label text,#mermaid-svg-Oa1yRh73uc2RUc3u .node .label text,#mermaid-svg-Oa1yRh73uc2RUc3u .image-shape .label,#mermaid-svg-Oa1yRh73uc2RUc3u .icon-shape .label{text-anchor:middle;}#mermaid-svg-Oa1yRh73uc2RUc3u .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Oa1yRh73uc2RUc3u .rough-node .label,#mermaid-svg-Oa1yRh73uc2RUc3u .node .label,#mermaid-svg-Oa1yRh73uc2RUc3u .image-shape .label,#mermaid-svg-Oa1yRh73uc2RUc3u .icon-shape .label{text-align:center;}#mermaid-svg-Oa1yRh73uc2RUc3u .node.clickable{cursor:pointer;}#mermaid-svg-Oa1yRh73uc2RUc3u .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Oa1yRh73uc2RUc3u .arrowheadPath{fill:#333333;}#mermaid-svg-Oa1yRh73uc2RUc3u .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Oa1yRh73uc2RUc3u .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Oa1yRh73uc2RUc3u .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Oa1yRh73uc2RUc3u .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Oa1yRh73uc2RUc3u .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Oa1yRh73uc2RUc3u .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Oa1yRh73uc2RUc3u .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Oa1yRh73uc2RUc3u .cluster text{fill:#333;}#mermaid-svg-Oa1yRh73uc2RUc3u .cluster span{color:#333;}#mermaid-svg-Oa1yRh73uc2RUc3u div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Oa1yRh73uc2RUc3u .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Oa1yRh73uc2RUc3u rect.text{fill:none;stroke-width:0;}#mermaid-svg-Oa1yRh73uc2RUc3u .icon-shape,#mermaid-svg-Oa1yRh73uc2RUc3u .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Oa1yRh73uc2RUc3u .icon-shape p,#mermaid-svg-Oa1yRh73uc2RUc3u .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Oa1yRh73uc2RUc3u .icon-shape .label rect,#mermaid-svg-Oa1yRh73uc2RUc3u .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Oa1yRh73uc2RUc3u .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Oa1yRh73uc2RUc3u .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Oa1yRh73uc2RUc3u :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Unicode 三明治
尽早解码
尽可能晚编码
输入字节读文件/接收网络数据
业务逻辑层纯文本字符串,再不解码编码
输出字节写文件/发送网络数据
在 Python 3 中,内置的 open 函数能在读取文件时自动解码,在写入文件时自动编码。
重要:open 函数应当在调用时显式指定 encoding 参数。依赖系统的默认编码会导致代码在不同环境下行为不一致。
文本文件处理最佳实践
# 显式指定编码(推荐)
with open('example.txt', 'r', encoding='utf-8') as f:
content = f.read() # 自动解码为 str
with open('output.txt', 'w', encoding='utf-8') as f:
f.write(content) # 自动编码为字节
# 避免这样写(依赖系统默认编码)
with open('example.txt', 'r') as f: # 可能在不同环境下出错
content = f.read()
八、为了正确比较而规范化 Unicode 字符串
8.1 规范化的重要性
Unicode 有组合字符(combining characters)——附加到前一个字符上的音调或其他标记,在打印时看似一个字符,但在底层可能是由多个码位组合而成的。下面是一个典型的范例。
# 同一个字符的不同表示方式
s1 = '\\xc7' # 预组合字符 U+00C7 'Ç'
s2 = 'C' + '\\u0327' # 基础字母 C + 组合符 U+0327 下软尾符
print(s1, s2) # Ç Ç(视觉上看起来一样)
print(s1 == s2) # False(码位序列不同)
# 标准化后就可以正确比较
import unicodedata
print(unicodedata.normalize('NFC', s1) == unicodedata.normalize('NFC', s2)) # True
Unicode 规范化规则的主要价值在于:让语义上相同的字符即使码位的表现形式不同,也能够在比较时被视为相等。
8.2 规范化方式
unicodedata.normalize 的第一个参数决定标准化方式:
| NFC | Normalization Form C | 将分解的字符组合成最短的等价形式(推荐) |
| NFD | Normalization Form D | 将组合字符分解为基础字符和单独的组合字符 |
| NFKC | Normalization Form KC | 在 NFC 的基础上增加兼容分解,如将 fi 分解为 fi |
| NFKD | Normalization Form KD | 在 NFD 的基础上增加兼容分解 |
import unicodedata
# NFC:组合(推荐)
s = 'é' # 预组合字符
nfc = unicodedata.normalize('NFC', s)
print(f"NFC: {nfc}")
# NFD:分解
nfd = unicodedata.normalize('NFD', s)
print(f"NFD: {nfd}")
# 兼容组合示例
s2 = 'fi' # 连字(兼容字符)
print(unicodedata.normalize('NFKC', s2)) # 'fi'(拆分为基础字母)
移除变音符号的实现方式:使用 NFD 规范化后,只保留 ASCII 部分的字符:
import unicodedata
def remove_diacritics(text):
"""移除文本中的变音符号(如 é → e)"""
nfd = unicodedata.normalize('NFKD', text)
return ''.join(c for c in nfd if not unicodedata.combining(c))
print(remove_diacritics('café')) # 'cafe'
print(remove_diacritics('São Paulo')) # 'Sao Paulo'
8.3 大小写折叠(case folding)
str.casefold() 是一个专用于不区分大小写匹配的方法,比 str.lower() 更激进。例如,德语字母 ß 经过 casefold() 后会变成 ss。
# lower() 与 casefold() 的区别
german = 'Straße'
print(german.lower()) # 'straße'
print(german.casefold()) # 'strasse'(更适合于不区分大小写的匹配)
# 不区分大小写的比较
def case_folded_equals(s1, s2):
return unicodedata.normalize('NFC', s1.casefold()) == unicodedata.normalize('NFC', s2.casefold())
九、Unicode 文本排序
Python 中排序任何类型的序列时,通过比较序列中各项的元素(默认基于码位)进行。这种按码位排序的方式对于非 ASCII 文本常常不符合人类预期的字母顺序。
# 按码位排序(通常不是想要的顺序)
fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola']
print(sorted(fruits)) # ['acerola', 'atemoia', 'açaí', 'cajá', 'caju']
locale.strxfrm 函数将字符串转换为可用于区域感知比较的形式,可作为 sorted 的 key 函数使用。
import locale
# 设置区域(需要系统支持)
locale.setlocale(locale.LC_ALL, 'pt_BR.UTF-8')
fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola']
sorted_fruits = sorted(fruits, key=locale.strxfrm)
print(sorted_fruits) # 符合葡萄牙语排序顺序
# 注意:locale 可能不可用或者在不同环境下实现不一致
# 替代方案:PyUCA(Unicode Collation Algorithm)库
对于跨平台一致性要求较高的场景,建议使用第三方库 PyUCA(Unicode Collation Algorithm)进行排序。
十一、支持字符串和字节序列的双模式 API
11.1 正则表达式中的双模式
Python 的 re 模块同时支持字符串模式(str)和字节序列模式(bytes),两者行为略有差异。
import re
# 字符串模式(处理 Unicode 字符)
text = 'café'
pattern = r'\\w+'
print(re.findall(pattern, text)) # ['café']
# 字节序列模式(处理 ASCII 数据范围)
byte_text = b'caf\\xc3\\xa9'
try:
# 字节模式不能直接匹配非 ASCII 字节,上面提到的正则表达式元字符只会匹配 ASCII 数据
print(re.findall(b'\\w+', byte_text))
except UnicodeDecodeError as e:
print(f"字节模式错误: {e}")
11.2 os 函数中的双模式
os 模块的一些函数可以接受字符串或字节序列路径,并根据参数类型返回相应类型的返回值。
import os
# 字符串模式
str_path = '.'
print(os.listdir(str_path)) # 返回字符串列表
# 字节序列模式
bytes_path = b'.'
print(os.listdir(bytes_path)) # 返回字节序列列表
十二、本章思维导图
#mermaid-svg-cqN4Sk32ofseCH5C{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cqN4Sk32ofseCH5C .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cqN4Sk32ofseCH5C .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cqN4Sk32ofseCH5C .error-icon{fill:#552222;}#mermaid-svg-cqN4Sk32ofseCH5C .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cqN4Sk32ofseCH5C .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cqN4Sk32ofseCH5C .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cqN4Sk32ofseCH5C .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cqN4Sk32ofseCH5C .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cqN4Sk32ofseCH5C .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cqN4Sk32ofseCH5C .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cqN4Sk32ofseCH5C .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cqN4Sk32ofseCH5C .marker.cross{stroke:#333333;}#mermaid-svg-cqN4Sk32ofseCH5C svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cqN4Sk32ofseCH5C p{margin:0;}#mermaid-svg-cqN4Sk32ofseCH5C .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-cqN4Sk32ofseCH5C .cluster-label text{fill:#333;}#mermaid-svg-cqN4Sk32ofseCH5C .cluster-label span{color:#333;}#mermaid-svg-cqN4Sk32ofseCH5C .cluster-label span p{background-color:transparent;}#mermaid-svg-cqN4Sk32ofseCH5C .label text,#mermaid-svg-cqN4Sk32ofseCH5C span{fill:#333;color:#333;}#mermaid-svg-cqN4Sk32ofseCH5C .node rect,#mermaid-svg-cqN4Sk32ofseCH5C .node circle,#mermaid-svg-cqN4Sk32ofseCH5C .node ellipse,#mermaid-svg-cqN4Sk32ofseCH5C .node polygon,#mermaid-svg-cqN4Sk32ofseCH5C .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cqN4Sk32ofseCH5C .rough-node .label text,#mermaid-svg-cqN4Sk32ofseCH5C .node .label text,#mermaid-svg-cqN4Sk32ofseCH5C .image-shape .label,#mermaid-svg-cqN4Sk32ofseCH5C .icon-shape .label{text-anchor:middle;}#mermaid-svg-cqN4Sk32ofseCH5C .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cqN4Sk32ofseCH5C .rough-node .label,#mermaid-svg-cqN4Sk32ofseCH5C .node .label,#mermaid-svg-cqN4Sk32ofseCH5C .image-shape .label,#mermaid-svg-cqN4Sk32ofseCH5C .icon-shape .label{text-align:center;}#mermaid-svg-cqN4Sk32ofseCH5C .node.clickable{cursor:pointer;}#mermaid-svg-cqN4Sk32ofseCH5C .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cqN4Sk32ofseCH5C .arrowheadPath{fill:#333333;}#mermaid-svg-cqN4Sk32ofseCH5C .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cqN4Sk32ofseCH5C .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cqN4Sk32ofseCH5C .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cqN4Sk32ofseCH5C .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cqN4Sk32ofseCH5C .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cqN4Sk32ofseCH5C .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cqN4Sk32ofseCH5C .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cqN4Sk32ofseCH5C .cluster text{fill:#333;}#mermaid-svg-cqN4Sk32ofseCH5C .cluster span{color:#333;}#mermaid-svg-cqN4Sk32ofseCH5C div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cqN4Sk32ofseCH5C .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cqN4Sk32ofseCH5C rect.text{fill:none;stroke-width:0;}#mermaid-svg-cqN4Sk32ofseCH5C .icon-shape,#mermaid-svg-cqN4Sk32ofseCH5C .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cqN4Sk32ofseCH5C .icon-shape p,#mermaid-svg-cqN4Sk32ofseCH5C .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cqN4Sk32ofseCH5C .icon-shape .label rect,#mermaid-svg-cqN4Sk32ofseCH5C .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cqN4Sk32ofseCH5C .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cqN4Sk32ofseCH5C .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cqN4Sk32ofseCH5C :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
第4章 文本和字节序列
Unicode基础
字符
码位 U+xxxx
编码 码位→字节
解码 字节→码位
二进制序列
bytes 不可变
bytearray 可变
元素 0-255切片类型不变长度1不退化
memoryview 零复制内存视图
struct 打包/解包C结构
编解码器
utf-8 最常用
latin1 / iso8859_1
gb2312 中文编码
UTF-16 含BOM
编解码错误
UnicodeEncodeErrorstr→bytes
UnicodeDecodeErrorbytes→str
chardet 编码检测
文本文件最佳实践
Unicode三明治
尽早解码
业务只用str
尽量晚编码
open显式指定encoding
文本规范化
NFD / NFC / NKFC / NFKD
casefold 大小写折叠
locale.strxfrm 区域排序
双模式API
re正则 str/bytes双模式
os函数 参数决定返回类型
十三、常见错误与最佳实践
常见错误
| UnicodeEncodeError | 字符串中包含目标编码不支持的字符 | 使用 errors 参数;或改用支持该字符的编码(如 UTF-8) |
| UnicodeDecodeError | 解码时使用了错误的编码 | 确定正确的编码再解码;使用 errors='replace' 显示占位符 |
| SyntaxError 源码中出现非 ASCII 字符 | Python 源文件编码与系统默认编码不一致 | Python 3 默认 UTF-8,需确保编辑器也以 UTF-8 保存 |
| 在 Python 2 代码中混用 str 与 bytes | Python 2 中 str 本质是 bytes,unicode 才是文本 | 升级到 Python 3,或在 Python 2 中使用 from __future__ import unicode_literals |
| 网络传输时对 bytes 再次编码 | 传输的数据已经是 bytes,不能再调用 encode() | 检查数据类型,bytes 直接发送,str 先编码 |
最佳实践总结
十四、小结
第 4 章“文本和字节序列”细致地处理了 Unicode 与二进制字符集的重要话题,建立起了从文本到字节的正确映射关系:
- str 和 bytes 之间的界限必须区分:str 是文本,bytes 是二进制数据。它们之间的转换必须显式完成
- 编码是把“人类使用的文本”变成“计算机处理的字节”的过程;解码是字节还原为文本的过程。
- Python 的 bytes 和 bytearray 支持对二进制数据的灵活处理,切片会保留原始类型
- memoryview 提供零复制共享内存访问能力,适合大批量二进制数据的高效操作
- struct 模块在二进制数据与 C 结构体之间解包/打包,处理底层字节流
- 处理文本的最佳实践是 Unicode 三明治——尽早解码,中间层只处理 str,尽可能晚编码
- Unicode 规范化(unicodedata.normalize)解决语义相同但码位不同造成的比较问题
- 排序非 ASCII 文本应考虑使用 locale.strxfrm 或 PyUCA 进行区域感知排序
Python 3 完全区分了人类可读的文本和底层的字节流,这是解决各种编码问题的根基。经过本章的学习,你应该能够轻松应对与字符和编码相关的错误,写出在各种平台上稳定运行的、不会随处乱码的程序。
十五、下一章预告
第 5 章《数据类构建器》(Data Class Builders)
在第 3 章熟悉了字典的强大扩展能力,在第 4 章掌握了文本和字节的区别之后,接下来我们将面临一个常见的问题:如何在 Python 中更方便地创建表示数据的简单类?
下一章将从以下角度展开:
- collections.namedtuple:一个轻量级的不可变类工厂,从第 2 章就见过,这里会深度挖掘它的局限和最佳使用场景
- typing.NamedTuple:带上类型注解的 namedtuple 变种,是静态分析和 IDE 辅助的好帮手
- @dataclass 装饰器(Python 3.7+):用更少的代码实现和普通类一样灵活的——同时支持可变属性、默认工厂、__post_init__ 钩子等方法
- 三种数据类构建器的对比:namedtuple vs NamedTuple vs dataclass
- 何时选用哪个构建器;如何为数据建模简化代码、提高可维护性
第 5 章将是编写 Pythonic 数据类的全方位指南,帮助你在日常的程序设计中明智地选择最合适的工具。
本文为个人学习笔记,仅用于知识分享。如有错误,欢迎指正。
👍🏻 点赞 + 收藏 + 分享,让更多开发者看到这篇深度解析!❤️ 如果觉得有用,请给个赞支持一下作者!




