CSV 能读出来还不够:别把空字符串、缺列和字段内换行混在一起
摘要:导入 CSV 时,逗号不一定是分隔符,一行文本也不一定是一条记录。本文用 Python 标准库的本地合成实验,说明引号、换行和字段数量分别影响什么,并给出一个可运行的结构校验示例。
业务明细导入常从 split(",") 开始。样例只有编号、名称、金额时,这种写法似乎够用;名称中一旦出现逗号,列就错位。更隐蔽的是,有些错误不会抛异常,而是悄悄改变数据含义。
先区分物理行和逻辑记录
本次样例中的 "a,b" 是一个字段,"said ""hi""" 则表示包含双引号的文本。标准库 csv.reader 分别读出 a,b 和 said "hi";直接按逗号切分第一条数据,会得到四段,而预期只有三列。
字段还可以包含换行。本次文件使用 CRLF,一条备注跨两行,因此四个物理行只对应表头和两条数据记录。先调用 splitlines() 再交给解析器,虽然仍能读出记录,却把备注中的换行丢掉,得到 line1line2。解析没有报错,内容已经改变。
读取真实文件时应写 open(path, encoding="utf-8", newline=""),让 CSV 模块处理换行。本次按这种方式读取,字段内保留 \r\n;使用默认的 newline=None,文本层会将它转换为 \n。是否允许规范化应由数据约定决定,不能把两者当作字节完全一致。
空字段有位置,缺字段没有位置
默认读取行为下,1,,10 中间的空字段和 2,"",20 中间的带引号空字段,都会得到空字符串。尾部逗号也有意义:3,tail, 提供三个字段,最后一个为空;6,only_two 只提供两个字段。
本次用 DictReader 读取短行,缺少的金额自动填成 None;多出的列则默认放到键 None 对应的列表里。这是解析器的容纳策略,并没有替导入程序确认结构有效。若再写 value or "",缺列和明确留空就会被合并。
需要固定三列时,可以先用列表读取,核对表头及每条记录的列数,再映射成字典:
import csv
import io
def load_csv(text):
expected = ["id", "note", "amount"]
reader = csv.reader(io.StringIO(text, newline=""), strict=True)
if next(reader, None) != expected:
raise ValueError("unexpected header")
result = []
for record_no, fields in enumerate(reader, 2):
if len(fields) != len(expected):
raise ValueError(
f"record {record_no}: expected 3 fields, got {len(fields)}"
)
result.append(dict(zip(expected, fields)))
return result
if __name__ == "__main__":
source = 'id,note,amount\r\n1,"a,b",10\r\n2,"line1\r\nline2",20\r\n'
for row in load_csv(source):
print(repr(row["note"]))
输出为 'a,b' 和 'line1\r\nline2'。这里的 record_no 从表头后的第二条逻辑记录开始计数,并非文件物理行号。表头采用精确匹配,列顺序改变、重复列名或空文件都会被拒绝;这是示例的输入契约。
严格模式仍需要业务校验
本次未闭合引号触发 csv.Error,短行、多列及重复表头由示例主动拒绝。但 strict=True 仍接受未加引号字段中的 ab"cd,也接受金额 not_a_number。因此它不能视为所有 CSV 规范的完整验证器,更不会检查金额、必填项或编号唯一性。
实际导入应在结构确认后,再约定空字符串是否有效、各列如何转换和错误如何定位。示例一次收集全部记录,适合解释小型固定格式文件;大文件还需另行设计大小限制、流式处理及失败后的提交规则。
例如,备注可留空而金额必填时,应分别校验这两列,不能只看整行字段数为三就接受。示例在内存中构建列表,遇到异常不会返回部分结果;若改为边读边入库,前面已写入的记录不会因后续解析失败自动撤回,需要另行安排事务或暂存机制。
2026年11月3日,本实验在 Windows、Python 3.12.14 下实际运行五组观察和四组预期拒绝用例,另执行文中代码。五组观察及代码退出0;四组坏输入退出2并产生预期错误,表示拒绝行为符合验收条件。原始输出、退出码和文件指纹已本地保存。全部数据均为合成内容,未调用云服务,也未验证生产导入流程。
- 点赞
- 收藏
- 关注作者
评论(0)