Python 读取 CSV:排查首列表头里的隐藏 BOM
同一份 CSV,在表格软件里看得到 name 列,Python 却报 KeyError: ‘name’。这种情况可以先看字段名的真实表示,不必立刻修改数据。
1. 先看 repr,不凭肉眼判断
下面的样例会在临时目录中构造一份带 UTF-8 BOM 的 CSV,不读取或修改业务文件。
import csv
from pathlib import Path
from tempfile import TemporaryDirectory
text = 'name,note\r\n小明,"上海,浦东"\r\n小红,"第一行\r\n第二行"\r\n'
with TemporaryDirectory() as folder:
path = Path(folder) / "sample.csv"
path.write_bytes(text.encode("utf-8-sig"))
with path.open(encoding="utf-8", newline="") as f:
reader = csv.DictReader(f)
print(repr(reader.fieldnames[0]))
row = next(reader)
print("name" in row)
本次自检输出:
'\ufeffname'
False
字段名实际上是 \ufeffname,和 name 不相等。示例中的字符串以 UTF-8 BOM 开头;使用普通 utf-8 解码时,开头的标记会成为字符串里的字符。
2. 在解码入口处理 BOM
对已确认是 UTF-8、可能带 BOM 的输入,可以改用 utf-8-sig:
with path.open(encoding="utf-8-sig", newline="") as f:
reader = csv.DictReader(f)
print(reader.fieldnames)
for row in reader:
print(row["name"], repr(row["note"]))
这段代码放在上例 TemporaryDirectory 的 with 块内运行。结果为:
['name', 'note']
小明 '上海,浦东'
小红 '第一行\r\n第二行'
utf-8-sig 在读取时会处理文件开头的可选 BOM;同一份不带 BOM 的 UTF-8 样例也能正常读取。这里解决的是文件开头的标记,不是清洗所有字段中的不可见字符。官方说明见 Python codecs 文档的 encodings.utf_8_sig 小节。
3. 保留 CSV 对换行和引号的处理
样例故意包含两种内容:字段内的逗号,以及用引号包裹的跨行字段。不要用 split(“,”) 或把每个物理行直接当成一条记录。
Python csv 文档建议文件对象以 newline=“” 打开,让 csv 模块处理换行。上面的实验也验证了跨行字段中的 CRLF 被保留。数据有自己的分隔符或引号约定时,应明确配置 csv 的对应参数。
4. 这不是编码自动识别
utf-8-sig 不会把 GBK、UTF-16 等编码自动变成 UTF-8。示例使用严格解码;遇到无法解码的数据应核对来源编码,不能靠 errors=“ignore” 静默丢失字符。
示例只演示读取合法的两列表格,不替代导入时的列名、必填值、重复表头、字段数量和业务类型校验。本次在本机 Python 3.9.6 中检查了带 BOM、不带 BOM、字段内逗号、跨行字段、仅表头和非法字节的情况;没有宣称覆盖所有 CSV 方言或在 Windows 上实测。
官方参考资料:
- Python csv:
docs.python.org/3/library/csv.html - Python codecs / encodings.utf_8_sig:
docs.python.org/3/library/codecs.html#encodings.utf_8_sig
本文由 AI 辅助整理,示例为本次独立构造并在本机执行的教学数据。
- 点赞
- 收藏
- 关注作者
评论(0)