在 Python 3.9 中,将 re.compile() 对象存入元组并打印或写入文件时,其字符串表示(str(re.compile(...)))会因内部实现限制被强制截断,且默认编码可能导致 Unicode 乱码;根本原因在于 re.Pattern.__repr__ 的长度限制及文件写入未指定 UTF-8 编码。
在 python 3.9 中,将 `re.compile()` 对象存入元组并打印或写入文件时,其字符串表示(`str(re.compile(...))`)会因内部实现限制被强制截断,且默认编码可能导致 unicode 乱码;根本原因在于 `re.pattern.__repr__` 的长度限制及文件写入未指定 utf-8 编码。
该问题并非数据丢失,而是显示与序列化层面的限制:re.Pattern 对象的 __repr__ 方法(被 str() 和 print() 调用)为避免过长输出,默认对正则模式字符串进行截断(通常约 50–100 字符),并在末尾添加省略号 ...。同时,若文件以默认编码(如 Windows 上的 cp1252)打开,含 Unicode 的正则源字符串(如 szerzödés)会被错误编码为 szerzödés 等乱码。
✅ 正确做法是:避免直接 str() 化 re.Pattern 对象,转而显式提取并持久化其可读、可复现的关键属性:
✅ 推荐方案:序列化为 (name, pattern_str, flags) 元组
import re
# 原始定义(保持可读性)
pattern_str = r'\b(szerzödéséről|szerzödást|szerzödésemet|szerzödésést|szerzödéséhez|szerzdés|szerzdésre|szerzödéseddel|szerzödésések|szerzdéssel|szerzödésedet|szerzdésen|szerzödésével|szerzödéseinken|szerzödéséssel|szerzödéséket|szerzödést|szerzödéseinek|szerzödéseire|szerzödéseink|szerzödésed|szerzödésein|szerzödésébe|szerzözédeseket)\b'
regs = [('szerzodes_felmondas', pattern_str, re.UNICODE)]
# 写入文件(安全 UTF-8)
with open('regs_test.txt', 'w', encoding='utf-8') as f:
for name, pat_str, flags in regs:
# 构造可复现的 re.compile 调用语句(便于调试/重载)
flag_names = []
if flags & re.UNICODE: flag_names.append('re.UNICODE')
if flags & re.IGNORECASE: flag_names.append('re.IGNORECASE')
# ... 其他 flag 处理
flags_expr = '|'.join(flag_names) if flag_names else '0'
f.write(f"('{name}', re.compile(r{pat_str!r}, {flags_expr}))\n")
✅ 运行时重建 Pattern(按需编译)
# 从文件读取后,动态编译(不存储已编译对象)
with open('regs_test.txt', 'r', encoding='utf-8') as f:
for line in f:
# 解析 name, pattern_str, flags(实际中建议用 ast.literal_eval 或 JSON)
# 示例:简单解析(生产环境请用更健壮方式)
if line.strip().startswith("('szerzodes_felmondas'"):
# 提取 pattern_str 和 flags(略,见上文生成逻辑)
compiled = re.compile(pattern_str, re.UNICODE)
# 使用 compiled 对象进行匹配...
⚠️ 注意事项
-
❌ 不要依赖 str(re.compile(...)) 的输出——它仅为调试设计,非持久化格式;
-
✅ 始终显式指定 encoding='utf-8' 打开文件,尤其处理含重音字符的正则;
立即学习“Python免费学习笔记(深入)”;
Content at Scale
SEO长内容自动化创作平台
下载
-
? 若需长期存储,推荐使用 json(需预处理 pattern 为字符串)或 pickle(仅限可信环境,因反序列化存在安全风险);
-
? 对于大型正则集合,可封装为类或配置模块,提升可维护性:
class RegexRegistry: def __init__(self): self._patterns = {} def add(self, name, pattern, flags=0): self._patterns[name] = (pattern, flags) def get(self, name): pat_str, flags = self._patterns[name] return re.compile(pat_str, flags)
综上,截断是 re.Pattern.__repr__ 的有意行为,而非 Bug;解决核心在于分离「定义」与「编译」:存储原始字符串和标志位,运行时按需编译,既保证可读性、可移植性,又规避所有截断与编码陷阱。