02-字符串进阶
本章代码基于 Python 3.11+ 编写
本章讲解字符串编码、二进制数据和实用技巧。
本章讲解字符串的底层存储机制:编码(字符→字节)、二进制数据类型(bytes/bytearray)及实用处理技巧。
概念铺垫
为什么需要了解字符串编码?一个真实的乱码场景
问题场景: 你从网络下载了一份中文文件,用 Python 读取后显示乱码,或者在不同系统间传输文字数据时出现错误。
不了解编码的困惑:
- 为什么同样的中文字符在不同地方显示不一样?
bytes和str有什么区别,为什么不能直接混用?- 如何正确读写包含中文的文件?
了解编码后的解决方案:
python
text = "你好,世界!"
# 编码:将字符串转为字节(用于传输/存储)
utf8_bytes = text.encode('utf-8')
# 解码:将字节还原为字符串(用于显示)
restored = utf8_bytes.decode('utf-8')
print(restored) # 你好,世界!这就是编码的价值:理解字符和字节的转换,避免乱码。
编码的最简用法
python
# 编码:str → bytes
text = "Hello"
b = text.encode('utf-8') # b'Hello'
# 解码:bytes → str
s = b.decode('utf-8') # 'Hello'L1 理解层:会用
字符串编码
字符串编码: 将人类可读的文本(str)转换为计算机存储的字节序列(bytes)的规则。
┌──────────────────────────────────────────────────┐
│ 编码与解码流程 │
│ │
│ str encode(编码) bytes │
│ "你好" ──────────────────→ b'\xe4\xbd\xa0...' │
│ │
│ bytes decode(解码) str │
│ b'\xe4...' ──────────────────→ "你好" │
│ │
│ 常用编码: │
│ UTF-8 → 支持所有语言,推荐使用 │
│ GBK/GB2312 → 中文系统常用(旧) │
│ ASCII → 仅英文字符 │
└──────────────────────────────────────────────────┘最简示例:
python
text = "你好"
b = text.encode('utf-8')
print(b) # b'\xe4\xbd\xa0\xe5\xa5\xbd'
print(b.decode('utf-8')) # 你好关键代码解释:
| 代码 | 含义 | 说明 |
|---|---|---|
text.encode('utf-8') | 字符串编码为字节 | 指定编码方式,不指定默认 utf-8 |
b.decode('utf-8') | 字节解码为字符串 | 必须用相同编码解码,否则乱码 |
编码和解码:
python
text = "你好,世界!"
# 编码:字符串 -> 字节
utf8_bytes = text.encode('utf-8')
print(utf8_bytes) # b'\xe4\xbd\xa0\xe5\xa5\xbd...'
# 解码:字节 -> 字符串
print(utf8_bytes.decode('utf-8')) # 你好,世界!Unicode 码点
Unicode 码点: 每个字符对应一个唯一的整数编号。
┌──────────────────────────────────────┐
│ ord() 与 chr() 互转 │
│ │
│ ord('A') → 65 (字符→码点) │
│ chr(65) → 'A' (码点→字符) │
│ ord('中') → 20013 │
│ chr(0x4E2D) → '中' (十六进制码点)│
└──────────────────────────────────────┘python
# 获取 Unicode 码点
char = '中'
print(ord(char)) # 20013
# 从码点获取字符
print(chr(20013)) # 中
print(chr(0x4E2D)) # 中bytes 和 bytearray(二进制数据)
什么是二进制数据
在 Python 中,bytes 和 bytearray 用于处理二进制数据(如文件、网络数据、图片等)。
┌─────────────────────────────────────────────────────┐
│ 字符串 vs 二进制数据 │
├─────────────────────────────────────────────────────┤
│ │
│ str(字符串): │
│ • 存储文本数据 │
│ • Unicode 编码 │
│ • 人类可读 │
│ • 例如:"Hello", "你好" │
│ │
│ bytes(字节): │
│ • 存储二进制数据 │
│ • 0-255 的整数序列 │
│ • 计算机可读 │
│ • 例如:b'Hello', b'\x48\x65\x6c\x6c\x6f' │
│ │
└─────────────────────────────────────────────────────┘bytes 类型
bytes 是不可变的二进制序列。
python
# 创建 bytes
b1 = b"Hello" # 字节字面量
b2 = bytes([72, 101, 108, 108, 111]) # 从整数列表创建
b3 = "你好".encode('utf-8') # 字符串编码
print(b1) # b'Hello'
print(b2) # b'Hello'
print(b3) # b'\xe4\xbd\xa0\xe5\xa5\xbd'
# 访问元素(返回整数)
print(b1[0]) # 72('H' 的 ASCII 码)
print(b1[1:3]) # b'el'(切片返回 bytes)
# 不可变
# b1[0] = 74 # TypeError: 'bytes' object does not support item assignmentbytearray 类型
bytearray 是可变的二进制序列。
python
# 创建 bytearray
ba = bytearray(b"Hello")
print(ba) # bytearray(b'Hello')
# 可修改
ba[0] = 74 # 'J'
print(ba) # bytearray(b'Jello')
# 追加
ba.append(33) # '!'
print(ba) # bytearray(b'Jello!')
# 转换为 bytes
b = bytes(ba)
print(b) # b'Jello!'bytes vs bytearray
┌─────────────────────────────────────────────────────┐
│ bytes vs bytearray 对比 │
├─────────────────────────────────────────────────────┤
│ │
│ bytes bytearray │
│ ────── ────────── │
│ 不可变 可变 │
│ 创建后不能修改 可以修改元素 │
│ 更安全 更灵活 │
│ 可作为字典键 不能作为字典键 │
│ │
│ 使用场景: 使用场景: │
│ • 网络传输 • 需要修改二进制数据 │
│ • 文件读取 • 构建二进制协议 │
│ • 哈希计算 • 动态生成二进制 │
│ │
└─────────────────────────────────────────────────────┘编码和解码:
python
from __future__ import annotations
# 字符串 → bytes
text: str = "你好,Python!"
utf8_bytes: bytes = text.encode('utf-8')
gbk_bytes: bytes = text.encode('gbk')
print(f"UTF-8: {utf8_bytes}")
print(f"GBK: {gbk_bytes}")
# bytes → 字符串
decoded: str = utf8_bytes.decode('utf-8')
print(decoded) # 你好,Python!
# 注意:编码和解码必须匹配
# utf8_bytes.decode('gbk') # 乱码或错误!常用操作:
python
# 十六进制转换
b = b'\x48\x65\x6c\x6c\x6f'
print(b.hex()) # '48656c6c6f'
# 从十六进制创建
b2 = bytes.fromhex('48656c6c6f')
print(b2) # b'Hello'
# 长度
print(len(b'Hello')) # 5
print(len('你好'.encode('utf-8'))) # 6(每个中文 3 字节)
# 拼接
b1 = b'Hello'
b2 = b' World'
print(b1 + b2) # b'Hello World'
# 包含检查
print(b'ell' in b'Hello') # True实际场景
场景1:读取二进制文件
python
def read_binary_file(filepath: str) -> bytes:
"""读取二进制文件"""
with open(filepath, 'rb') as f: # 'rb' = read binary
return f.read()
def write_binary_file(filepath: str, data: bytes) -> None:
"""写入二进制文件"""
with open(filepath, 'wb') as f: # 'wb' = write binary
f.write(data)场景2:处理网络数据
python
# 模拟接收网络数据
received: bytes = b'\x00\x05Hello'
# 解析协议头
length: int = int.from_bytes(received[:2], 'big') # 5
data: bytes = received[2:2+length] # b'Hello'
print(f"长度: {length}, 数据: {data}")场景3:Base64 编码
python
import base64
# 编码
text: str = "Hello, Python!"
encoded: bytes = base64.b64encode(text.encode('utf-8'))
print(encoded) # b'SGVsbG8sIFB5dGhvbiE='
# 解码
decoded: bytes = base64.b64decode(encoded)
print(decoded.decode('utf-8')) # Hello, Python!正则表达式
基本使用
python
import re
# 查找所有匹配
text = "My phone: 123-456-7890"
phones = re.findall(r'\d{3}-\d{3}-\d{4}', text)
print(phones) # ['123-456-7890']
# 替换
text = "Hello World"
result = re.sub(r'\s+', ' ', text) # 多空格变单空格
print(result) # Hello World
# 分割
text = "apple, banana; orange"
parts = re.split(r'[,\s;]+', text)
print(parts) # ['apple', 'banana', 'orange']常用模式
┌──────────┬──────────────────────────────┐
│ 模式 │ 说明 │
├──────────┼──────────────────────────────┤
│ \d │ 数字 │
│ \w │ 字母、数字、下划线 │
│ \s │ 空白字符 │
│ . │ 任意字符(除换行) │
│ * │ 0 次或多次 │
│ + │ 1 次或多次 │
│ ? │ 0 次或 1 次 │
│ [] │ 字符集合 │
│ () │ 分组 │
└──────────┴──────────────────────────────┘实用示例
python
import re
# 提取邮箱
def extract_emails(text):
pattern = r'\b[\w.]+@[\w.]+\.\w+\b'
return re.findall(pattern, text)
# 验证手机号
def is_valid_phone(phone):
pattern = r'^1[3-9]\d{9}$'
return bool(re.match(pattern, phone))
# 提取 URL
def extract_urls(text):
pattern = r'https?://[\w./-]+'
return re.findall(pattern, text)实用技巧
文本清理
python
def clean_text(text):
"""清理文本:去除空白、标准化空格"""
text = text.strip()
text = " ".join(text.split())
return text
print(clean_text(" Hello World ")) # Hello World命名转换
python
import re
def snake_to_camel(name):
"""snake_case 转 camelCase"""
parts = name.split("_")
return parts[0] + "".join(word.capitalize() for word in parts[1:])
def camel_to_snake(name):
"""camelCase 转 snake_case"""
return re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()
print(snake_to_camel("hello_world")) # helloWorld
print(camel_to_snake("helloWorld")) # hello_world截断文本
python
def truncate(text, length=50, suffix="..."):
"""截断文本到指定长度"""
if len(text) <= length:
return text
return text[:length - len(suffix)] + suffix
print(truncate("这是一个很长的文本", 10)) # 这是一个很...L2 实践层:用好
推荐做法
| 做法 | 原因 | 示例 |
|---|---|---|
| 正则对象预编译 | 避免每次调用重复解析模式 | EMAIL_RE = re.compile(r"...") |
| 用 raw string 写正则 | 不需要双重转义反斜杠 | r"\d{3}-\d{4}" |
| 读写文件明确指定编码 | 避免跨平台编码不一致 | open("f.txt", encoding="utf-8") |
用 bytes 处理二进制数据 | 类型安全,防止 str/bytes 混用 | data: bytes = f.read() |
需要修改时用 bytearray | 避免频繁创建新 bytes 对象 | ba = bytearray(data) |
re.match 配合原始字符串 | 从起始判断可读性更好 | re.match(r"^1[3-9]\d{9}$", phone) |
用户可见数据编码错误用 replace | 保留出问题痕迹 | data.decode("utf-8", errors="replace") |
内部数据编码错误用 strict | 快速暴露编码异常 | data.decode("utf-8", errors="strict") |
反模式:不要这样做
正则表达式重复编译
python
# ❌ 错误做法:每次调用都重新编译正则
def extract_emails(text: str) -> list[str]:
return re.findall(r'\b[\w.]+@[\w.]+\.\w+\b', text)
def is_valid_email(email: str) -> bool:
return bool(re.match(r'^[\w.]+@[\w.]+\.[a-z]{2,}$', email))
# 问题:
# 1. 每次调用都重新解析和编译正则模式
# 2. 在高频调用的场景(循环、请求处理)性能浪费明显
# 3. 正则模式散落各处,难以统一管理python
# ✅ 正确做法:预编译正则对象
import re
EMAIL_PATTERN = re.compile(r'\b[\w.]+@[\w.]+\.\w+\b')
EMAIL_VALID = re.compile(r'^[\w.]+@[\w.]+\.[a-z]{2,}$')
def extract_emails(text: str) -> list[str]:
return EMAIL_PATTERN.findall(text)
def is_valid_email(email: str) -> bool:
return bool(EMAIL_VALID.match(email))路径中的反斜杠陷阱
python
# ❌ 错误做法:普通字符串中的反斜杠被转义
path = "C:\Users\new_files\data.txt"
# 实际值: C:\Users
# ew_files\data.txt
# \n 被解释为换行符,\f 被解释为换页符!
# ❌ 错误做法:手动转义反斜杠
path = "C:\\Users\\new_files\\data.txt"
# 勉强可用,但冗长易遗漏python
# ✅ 正确做法:用 raw string
path = r"C:\Users\new_files\data.txt" # 反斜杠字面保留
# ✅ 更好的做法:用 pathlib(跨平台)
from pathlib import Path
path = Path("C:/Users/new_files/data.txt") # Windows 也接受正斜杠编码错误处理不当
python
# ❌ 错误做法:不指定编码,依赖系统默认
with open("data.txt") as f:
content = f.read() # Windows 上可能用 GBK,Linux 上用 UTF-8
# 问题:同一份代码在不同系统上行为不一致,导致乱码
# ❌ 错误做法:忽略编码错误
text = data.decode("utf-8", errors="ignore") # 悄悄丢弃无法解码的字节!
# 问题:丢失数据且没有错误提示,静默失败python
# ✅ 正确做法:显式指定编码
with open("data.txt", encoding="utf-8") as f:
content = f.read()
# ✅ 正确做法:用 errors="replace" 保留线索
text = data.decode("utf-8", errors="replace") # 非法字节用 � 替换
# 至少能看到哪里有编码问题
# ✅ 正确做法:严格模式用于内部数据
text = data.decode("utf-8", errors="strict") # 编码错误立即抛异常
# 内部系统数据不应有编码问题,严格模式快速暴露 bugstr 和 bytes 混用
python
# ❌ 错误做法:str 和 bytes 混合操作
text = "hello"
data = b"world"
# result = text + data # TypeError!
# ❌ 错误做法:用 str 读写二进制文件
with open("image.png", "r") as f: # 二进制文件不该用文本模式
data = f.read() # 可能因编码错误而失败python
# ✅ 正确做法:明确区分文本和二进制
text = "hello"
data = b"world"
text += data.decode("utf-8") # 先解码再拼接
# ✅ 正确做法:二进制文件用二进制模式
with open("image.png", "rb") as f:
data: bytes = f.read()适用场景
| 场景 | 是否推荐 | 原因 |
|---|---|---|
| 高频调用的正则(循环、请求处理) | ✅ 预编译 | 编译一次,复用多次,性能提升 3-10x |
| 一次性匹配 | ❌ 不需要手动预编译 | re 模块内部自动缓存最近 ~512 个模式 |
| Windows 文件路径 | ✅ raw string | 反斜杠不被转义,路径字面清晰 |
| 跨平台路径 | ✅ pathlib | 消除 OS 差异 |
| 与外部系统交互的数据 | ✅ 明确指定编码 | 避免隐式依赖系统默认编码 |
| 纯内部系统数据 | ✅ UTF-8 + strict | 快速发现编码异常,不容忍错误 |
| 网络数据解析 | ✅ bytes + struct | 类型安全,协议解析清晰 |
| 需要频繁修改的二进制数据 | ✅ bytearray | 支持原地修改,避免重复创建 |
| 只读二进制数据(文件读取、哈希计算) | ✅ bytes | 不可变性保证数据不被意外修改 |
字符串构建性能(回顾)
python
# ❌ 不推荐:循环中使用 +
result = ""
for i in range(1000):
result += str(i) # 每次创建新字符串
# ✅ 推荐:使用 join
result = "".join(str(i) for i in range(1000))
# ✅ 推荐:列表收集后 join
parts = []
for i in range(1000):
parts.append(str(i))
result = "".join(parts)| 方式 | 时间复杂度 | 内存 | 推荐场景 |
|---|---|---|---|
+ 拼接少量字符串 | O(n) | 临时对象少 | ≤ 5 个字符串 |
join() 拼接大量 | O(n) 总长度 | 一次分配 | ≥ 10 个元素 |
io.StringIO | O(n) | 动态缓冲区 | 需要多次追加、回退的复杂构建 |
L3 专家层:深入
Python 如何实现编码
UTF-8 变长编码原理
UTF-8 是 Unicode 的变长编码方案,根据码点范围使用 1-4 字节:
UTF-8 编码规则:
┌─────────────────────────────────────────────────────────────┐
│ Unicode 码点范围 UTF-8 字节序列 字节数 │
│ ───────────────────────────────────────────────────── │
│ U+0000 ~ U+007F 0xxxxxxx 1 字节 │
│ (ASCII 字符 — 英文、数字、基本符号) │
│ │
│ U+0080 ~ U+07FF 110xxxxx 10xxxxxx 2 字节 │
│ (拉丁扩展、阿拉伯文、希腊文) │
│ │
│ U+0800 ~ U+FFFF 1110xxxx 10xxxxxx 3 字节 │
│ (BMP 字符 — 中文、日文、韩文) 10xxxxxx │
│ │
│ U+10000 ~ U+10FFFF 11110xxx 10xxxxxx 4 字节 │
│ (非 BMP — emoji、罕见汉字) 10xxxxxx 10xxxxxx │
│ │
│ 实测示例: │
│ 'A' (U+0041) → \x41 → 1 字节 │
│ 'é' (U+00E9) → \xC3\xA9 → 2 字节 │
│ '中' (U+4E2D) → \xE4\xB8\xAD → 3 字节 │
│ '😂' (U+1F602) → \xF0\x9F\x98\x82 → 4 字节 │
│ │
│ 设计特点: │
│ • 向后兼容 ASCII:纯 ASCII 文本的 UTF-8 就是 ASCII 本身 │
│ • 无字节序问题:UTF-8 是字节流,不存在大端/小端 │
│ • 自同步:任意字节的位置都可判断是否是字符起始字节 │
└─────────────────────────────────────────────────────────────┘验证代码:
python
import sys
# 不同语言字符的 UTF-8 字节数
chars = [
("英文字符 'A'", "A"),
("中文 '中'", "中"),
("emoji '😂'", "😂"),
]
for label, ch in chars:
encoded = ch.encode("utf-8")
print(f"{label}:")
print(f" 码点: U+{ord(ch):04X}")
print(f" UTF-8 字节: {encoded.hex(' ')}")
print(f" 字节数: {len(encoded)}")
print()
# 字节数差异:同一个 "字符" 在内存中的表现不同
text = "A中😂"
print(f"字符数: {len(text)}") # 3(3 个码点)
print(f"UTF-8 字节数: {len(text.encode('utf-8'))}") # 8(1+3+4 字节)
# Python 内部存储 vs UTF-8 编码
print(f"\nstr 对象内存: {sys.getsizeof(text)} 字节")
print(f"UTF-8 编码后: {len(text.encode('utf-8'))} 字节")Python str 内部编码验证
python
import sys
# Python 根据字符串内容自动选择最紧凑的内部编码
ascii_only = "hello" # 全 ASCII → 1 字节/字符
bmp_only = "你好世界" # 全 BMP → 2 字节/字符
has_emoji = "Hello 😂" # 含非 BMP → 4 字节/字符
print(f"纯 ASCII(5 字符): {sys.getsizeof(ascii_only)} 字节")
print(f"纯 BMP(4 字符): {sys.getsizeof(bmp_only)} 字节")
print(f"含 Emoji(7 字符): {sys.getsizeof(has_emoji)} 字节")
# 紧凑存储节省内存 — 大规模文本对比
big_ascii = "a" * 10000
big_chinese = "中" * 10000
print(f"\n10K ASCII: {sys.getsizeof(big_ascii)} 字节(~{sys.getsizeof(big_ascii)/1024:.0f} KB)")
print(f"10K 中文: {sys.getsizeof(big_chinese)} 字节(~{sys.getsizeof(big_chinese)/1024:.0f} KB)")
# 中文字符的内存占用约是 ASCII 的 2 倍(内部 UCS-2 vs Latin-1)正则表达式回溯性能
回溯原理
正则引擎在匹配失败时会回溯到上一个决策点尝试其他路径。这一机制在正常模式下是灵活的,但嵌套量词可能导致灾难性性能:
回溯示意 — 模式 r"(a|ab)+c" 匹配 "abc":
┌─────────────────────────────────────────────────────────────┐
│ 步骤 1: (a|ab) 匹配 "a" ✓ → 剩余 "bc" 待匹配 │
│ 步骤 2: (a|ab) 无法匹配 "b" 开头的 "bc" → 回溯,尝试 ab │
│ 步骤 3: ab 匹配 "ab" ✓ → 剩余 "c" 待匹配 │
│ 步骤 4: c 匹配 "c" ✓ → 匹配成功 │
│ │
│ 危险模式:r"(x+)+y" 匹配 "xxxxxxxx"(不含 y) │
│ → 每增加一个 x,回溯步骤呈指数增长!这称为 ReDoS 攻击 │
│ → 20 个 x:毫秒级;30 个 x:可能数秒;40 个 x:数分钟 │
└─────────────────────────────────────────────────────────────┘验证代码:
python
import re
import timeit
# 编译两种方式:内联 vs 预编译
COMPILED = re.compile(r"\d{3}-\d{3}-\d{4}")
def search_inline():
pattern = r"\d{3}-\d{3}-\d{4}"
return re.search(pattern, "Phone: 123-456-7890")
def search_compiled():
return COMPILED.search("Phone: 123-456-7890")
# 性能对比
print("正则编译 vs 内联性能(100K 次):")
print(f" 内联每次编译: {timeit.timeit(search_inline, number=100000):.4f}s")
print(f" 预编译: {timeit.timeit(search_compiled, number=100000):.4f}s")
# 危险回溯示例(仅作学习,执行可能很慢)
# ⚠️ 下面的代码可能极慢,仅供理解回溯机制
# re.match(r"(x+)+y$", "x" * 30) # 嵌套量词导致指数级回溯!编码/解码性能验证
python
import timeit
text = "你好,世界!Python 编程" * 1000
# encode 性能
t_encode = timeit.timeit(
"text.encode('utf-8')",
globals={"text": text},
number=10000
)
print(f"UTF-8 编码(54KB × 10,000 次): {t_encode:.4f}s")
# decode 性能
encoded = text.encode("utf-8")
t_decode = timeit.timeit(
"encoded.decode('utf-8')",
globals={"encoded": encoded},
number=10000
)
print(f"UTF-8 解码(78KB × 10,000 次): {t_decode:.4f}s")性能考量
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
s.encode('utf-8') | O(n) | 遍历每个码点,计算 UTF-8 字节序列 |
b.decode('utf-8') | O(n) | 解析字节流,重建 Unicode 码点 |
| ASCII 编码/解码 | O(n) | 逐字节复制,无码点转换,略快于 UTF-8 |
re.compile() | O(m),m = 模式长度 | 编译成内部状态机,一次成本 |
re.search()(预编译) | O(n) 平均 | 使用编译后的自动机顺序扫描 |
re.search()(内联) | O(m + n) | 每次多花 m 时间编译模式 |
| 危险回溯正则 | O(2^n) 最坏 | 嵌套量词匹配失败时指数级回溯 |
bytes.hex() | O(n) | 每个字节转 2 个十六进制字符 |
int.from_bytes() | O(1) | 固定字节数,直接位运算 |
base64.b64encode() | O(n) | 每 3 字节编为 4 字符 |
设计动机
| 设计选择 | 原因 | 实际影响 |
|---|---|---|
| str 与 bytes 严格分离(Python 3) | 消除 Python 2 隐式编解码导致的乱码 | 必须显式 encode/decode,边界清晰 |
| str 内部紧凑存储 | ASCII 文本占 1 字节/字符,比 Python 2 节省约 50% 内存 | 英文为主的应用内存显著降低 |
re 模块自动缓存最近 ~512 个模式 | 减少用户手动 compile 的心智负担 | 多数一次性场景无需手动 compile |
| UTF-8 作为默认编码 | 全球通用,向后兼容 ASCII | 处理中文无需额外配置 |
开放 errors 参数(strict/replace/ignore) | 用户可根据场景选择容错策略 | 避免一刀切的崩溃或静默丢失数据 |
| 正则引擎基于回溯而非 DFA | 支持反向引用、捕获组等高级特性 | 功能强大,但不当使用导致 ReDoS 风险 |
知识关联
字符串进阶知识关联:
┌─────────────────┐
│ Unicode 标准 │
│ (ISO 10646) │
└─────────────────┘
│
┌─────────┼─────────┐
↓ ↓ ↓
┌─────────┐ ┌─────────┐ ┌─────────┐
│ UTF-8 │ │ UTF-16 │ │ UTF-32 │
│ (变长) │ │ (变长) │ │ (定长) │
└─────────┘ └─────────┘ └─────────┘
│
↓
┌─────────────┐ ┌─────────────────┐
│ str 类型 │←───→│ bytes 类型 │
│ (内部存储) │ │ (外部表示) │
└─────────────┘ └─────────────────┘
│ │
↓ ↓
┌─────────────┐ ┌─────────────────┐
│ ord/chr │ │ 文件/网络 I/O │
│ (码点操作) │ │ (字节流处理) │
└─────────────┘ └─────────────────┘
│
↓
┌─────────────────┐
│ bytearray │
│ (可变二进制) │
└─────────────────┘
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 正则表达式 │────→│ NFA/DFA 自动机 │────→│ 回溯机制 │
│ (re 模块) │←────│ (匹配引擎) │←────│ (性能关键) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
↓
┌─────────────────┐ ┌─────────────────┐
│ re.compile() │ │ 灾难性回溯 │
│ (预编译缓存) │ │ (ReDoS 攻击) │
└─────────────────┘ └─────────────────┘前置依赖:字符串基础(str 类型、索引、切片、方法)
↓
当前概念:字符串进阶(编码、bytes/bytearray、正则表达式)
↓
后续依赖:
├── 文件 I/O(文本/二进制读写,编码指定)
├── 网络编程(HTTP 请求/响应中的字节处理)
├── 数据科学(CSV/JSON 文本解析、数据清洗)
├── Web 开发(请求/响应编码、模板引擎)
└── 系统编程(协议解析、二进制格式处理)本章小结
┌─────────────────────────────────────────────────────────────┐
│ 字符串进阶 知识要点 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 编码: │
│ ✓ encode() / decode() │
│ ✓ ord() / chr() │
│ │
│ 二进制数据: │
│ ✓ bytes:不可变二进制序列 │
│ ✓ bytearray:可变二进制序列 │
│ ✓ hex() / fromhex():十六进制转换 │
│ ✓ 文件读写:'rb' / 'wb' 模式 │
│ │
│ 正则表达式: │
│ ✓ re.findall() / re.sub() / re.match() │
│ ✓ 常用模式:\d \w \s . * + ? │
│ │
│ 性能优化: │
│ ✓ 使用 join 而非 + 连接大量字符串 │
│ ✓ 高频正则预编译为 re.compile 对象 │
│ │
└─────────────────────────────────────────────────────────────┘