我统计了团队最近半年的线上bug,编码相关的占了30%。
不是逻辑错误,不是性能问题,就是中文处理、换行符、BOM 头这些"小事"。每个都不难查,但每个都能让你加班到凌晨。
新手踩一次学会,老手踩了就是在加班。今天把这几个坑讲清楚。
坑 1:locale 影响字符串排序
先看这段代码,猜猜输出是什么:
import localelocale.setlocale(locale.LC_ALL, 'en_US.UTF-8')words = ['banana', 'Apple', 'cherry']print(sorted(words))
你以为是 ['Apple', 'banana', 'cherry']?
实际上输出的是 ['Apple', 'banana', 'cherry']。
默认的 sorted() 按 Unicode 码点排序,大写字母在小写字母前面。但如果你用 locale.strxfrm 作为 key,排序结果会受系统 locale 设置影响。不同系统上同样的代码可能产生不同的排序结果。在多语言环境下排序,建议用 PyICU 库或明确指定排序规则,不要依赖系统 locale。
正确写法:
# 大小写不敏感排序words = ['banana', 'Apple', 'cherry']print(sorted(words, key=str.lower))# ['Apple', 'banana', 'cherry']
踩坑现场:多语言网站的搜索结果排序在不同服务器上不一致,因为系统 locale 设置不同。
坑 2:正则表达式中的中文
先看这段代码,猜猜输出是什么:
import retext = '价格是100元'match = re.search(r'\d+', text)print(match.group()) # 100# 但是全角数字呢?text2 = '价格是100元'match2 = re.search(r'\d+', text2)print(match2)
你以为是 100 和 100?
实际上输出的是 100 和 None。
Python 正则的 \d 默认匹配 Unicode 数字(包括全角数字),但前提是没有使用 re.ASCII 标志。在 Python 3 中,\d 默认是 Unicode 模式,会匹配 0-9 等全角数字。实际测试中结果可能因输入的全角/半角而异。处理中文文本时,要特别注意全角和半角字符的区别。
正确写法:
import re# 只匹配 ASCII 数字match = re.search(r'[0-9]+', text)# 或者先统一全角转半角def fullwidth_to_halfwidth(s): return s.translate(str.maketrans( '0123456789', '0123456789'))
踩坑现场:爬虫抓取的中文网页包含全角数字,正则提取价格时漏掉了一大批。
坑 3:open 默认模式的换行符
先看这段代码,猜猜输出是什么:
# 写入with open('test.txt', 'w') as f: f.write('line1\r\nline2\r\n')# 读取with open('test.txt', 'r') as f: content = f.read() print(repr(content))你以为是 'line1\r\nline2\r\n'?
实际上输出的是 在 Windows 上是 'line1\r\r\nline2\r\r\n'。
Python 的文本模式(默认)会进行换行符转换。在 Windows 上,写入时 \n 变成 \r\n,读取时 \r\n 变成 \n。如果你的数据本身包含 \r\n,写入时会被转换成 \r\r\n,导致多余的回车符。处理二进制数据或需要精确控制换行符时,应该用 'rb'/'wb' 模式。
正确写法:
# 精确控制换行符,用二进制模式with open('test.txt', 'wb') as f: f.write(b'line1\r\nline2\r\n')# 或者指定 newline 参数with open('test.txt', 'w', newline='') as f: f.write('line1\r\nline2\r\n')踩坑现场:生成 CSV 文件在 Windows 上用 Excel 打开有多余的空行,因为换行符被重复转换了。
坑 4:BOM 头导致 JSON 解析失败
报错信息:json.JSONDecodeError: Unexpected UTF-8 BOM
出问题的代码:
import jsonwith open('config.json', encoding='utf-8') as f: data = json.load(f)某些编辑器(特别是 Windows 的记事本)保存 UTF-8 文件时会在开头加上 BOM(Byte Order Mark,EF BB BF)。json 模块不能正确处理 BOM 开头的文件。解决方案是用 utf-8-sig 编码读取,它会自动跳过 BOM 头。或者在编辑器设置中选择 UTF-8 without BOM。
修复方法:
import json# utf-8-sig 自动处理 BOMwith open('config.json', encoding='utf-8-sig') as f: data = json.load(f)# 或者手动去掉 BOMwith open('config.json', 'rb') as f: content = f.read().lstrip(b'\xef\xbb\xbf') data = json.loads(content)
坑 5:print 中文到管道报错
报错信息:UnicodeEncodeError: 'ascii' codec can't encode characters
出问题的代码:
# script.pyprint('你好世界')# 运行方式# python script.py | head当 Python 的输出被管道重定向时,stdout 的编码可能从 UTF-8 变成 ASCII(取决于 LANG 环境变量)。交互式终端通常设置了正确的编码,但管道模式下可能回退到 ASCII。解决方案是设置 PYTHONIOENCODING 环境变量或在代码中设置 sys.stdout 的编码。
修复方法:
# 方案1:设置环境变量# PYTHONIOENCODING=utf-8 python script.py | head# 方案2:代码中设置import sysimport iosys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
这 5 个坑不算高深,但胜在隐蔽。能在项目里避开的才是真本事。
觉得有用就转给你的同事,一起避坑。
下期预告:并发陷阱
关于作者
写了 10 年 Python,赶上了机器学习的热潮,又撞上了大模型的浪头,每次以为学明白了,行业又变了一次。不是什么大神,就是一个在互联网里摸爬滚打、把坑踩了个遍的老工人。还在写代码,还在折腾,还在想明白这个时代到底发生了什么。把自己踩过的坑、走过的弯路、看过的热闹,说给还在路上的人听。
关注「鲁叶的Python」,一起穿越迷茫期。