跳到正文
效率工具实用笔记关于本站
表格与数据

CSV 分隔符不同怎么办:识别方言与读取检查

约 9 分钟阅读AI 辅助整理

CSV 没有统一标准,不同程序可能用逗号、制表符、分号或空格分隔。本文用自建示例演示如何用 csv.Sniffer 识别方言、用 list_dialects 查看已注册方言,并检查读取结果是否正确。

本文要点

  1. csv 模块提供了 Sniffer 类,用来推断 CSV 文件的格式

  2. Sniffer 推断出方言后,可以把它传给 csv.reader

  3. 如果你已经知道文件用什么分隔,可以直接在 reader 里指定 delimiter,不必先推断

CSV 分隔符不同怎么办:识别方言与读取检查的知识要点示意图
依据已审核正文摘取要点制作的示意图,非实物照片;请结合正文中的条件与范围阅读。要点摘自已审核正文;完整条件与说明见正文。查看高清图解 ↗
本文目录

先看现象:分隔符不同会让读取结果变成什么样

如果你打开一个 CSV 文件,发现整行内容挤在一列里,或者每行被拆成了数量不对的字段,先别急着改数据。这通常说明文件实际使用的分隔符和你读取时假定的分隔符不一致。

CSV 格式在 RFC 4180 之前已经使用多年,但一直没有严格统一的标准,所以不同应用生成和消费的 CSV 数据常存在细微差异。这些差异包括分隔符和引号字符的不同。csv 模块的设计目标之一,就是让你不必了解某个程序具体用了什么格式,也能读写它的数据。

本文只处理一个具体问题:当你拿到一个分隔符不是逗号的自建 CSV 文件时,怎么识别它用了什么分隔符,以及怎么检查读取结果对不对。下面所有文件都是练习用的自建示例,不涉及真实业务数据。

开始前请先复制一份文件作为练习副本,不要拿重要数据直接做实验。

用 Sniffer 从样本里推断分隔符

csv 模块提供了 Sniffer 类,用来推断 CSV 文件的格式。它的 sniff 方法分析给定的样本,返回一个反映所发现参数的 Dialect 子类。

下面是一个自建示例。假设你有一个用分号分隔的练习文件 exercise/sample_semicolon.csv,内容如下:

name;age
Alice;30
Bob;25

用 Sniffer 推断它的方言:

import csv

with open('exercise/sample_semicolon.csv', newline='') as f:
    sample = f.read(1024)
    dialect = csv.Sniffer().sniff(sample)
    print(dialect.delimiter)

sniff 的 sample 参数是一段文本样本。如果传入可选的 delimiters 参数,它会被解释为一个包含可能有效分隔符字符的字符串。

如果多个分隔符对样本的拆分效果一样好——比如逗号和分号都能一致地拆分每一行——Sniffer 会优先选择 preferred 属性中列出的分隔符,按该顺序,不管每个分隔符出现了多少次。preferred 的初始值是逗号、制表符、分号、空格、冒号,这个列表可以修改。

这里要提醒一点:sniff 是基于样本的推断,不是对文件格式的权威判定。样本太短或内容太规整时,推断结果可能和实际不符。所以推断出分隔符后,还要做下一步的读取检查。

推断出方言后,怎么用它读取并检查结果

Sniffer 推断出方言后,可以把它传给 csv.reader。注意读取前要把文件位置移回开头,因为 sniff 已经读走了一段样本。

import csv

with open('exercise/sample_semicolon.csv', newline='') as f:
    dialect = csv.Sniffer().sniff(f.read(1024))
    f.seek(0)
    reader = csv.reader(f, dialect)
    for row in reader:
        print(row)

打开文件时要用 newline=''。如果不指定,引号字段里嵌入的换行不会被正确解释,在写入时使用 \r\n 行尾的平台上还会多出一个 \r。csv 模块自己做换行处理,所以指定 newline='' 总是安全的。

读取结果里,每一行返回的是一个字符串列表,不会自动做数据类型转换。所以 age 列读出来是字符串 '30',不是数字 30。

检查读取结果是否正确的办法:看每行的字段数量是否一致,看表头是否单独成行,看原本应该分开的值有没有被拆开或粘在一起。如果字段数量对不上,说明分隔符可能推断错了。

不想用 Sniffer 时,怎么手动指定分隔符

如果你已经知道文件用什么分隔,可以直接在 reader 里指定 delimiter,不必先推断。

import csv

with open('exercise/sample_semicolon.csv', newline='') as f:
    reader = csv.reader(f, delimiter=';')
    for row in reader:
        print(row)

delimiter 是一个单字符字符串,用来分隔字段,默认是逗号。除了 delimiter,还可以用其他 fmtparams 关键字参数覆盖当前方言里的单个格式参数。

手动指定的好处是结果可预期,不依赖样本推断。代价是你得先知道文件实际用了什么分隔符。如果你不确定,可以先按上一节用 Sniffer 推断,再把推断结果和手动指定的结果对照。

这里只演示 delimiter 这一个参数。其他格式参数不在本文范围内,需要时请查阅 csv 模块文档的 Dialects and formatting parameters 一节。

用 list_dialects 查看已注册的方言名

除了逐个指定参数,csv 模块还允许用方言名来指定一组格式参数。csv.list_dialects() 返回所有已注册方言的名称。

import csv

print(csv.list_dialects())

模块内置注册了几个方言。excel 类定义了 Excel 生成的 CSV 文件的常见属性,注册名是 'excel'。excel_tab 类定义了 Excel 生成的制表符分隔文件的常见属性,注册名是 'excel-tab'。unix_dialect 类定义了 UNIX 系统上生成的 CSV 文件的常见属性,即使用 \n 作为行终止符并给所有字段加引号,注册名是 'unix'。

reader 和 writer 的 dialect 参数可以是 Dialect 子类的实例,也可以是 list_dialects() 返回的字符串之一。所以如果你的文件是制表符分隔的,可以这样读:

import csv

with open('exercise/sample_tab.csv', newline='') as f:
    reader = csv.reader(f, dialect='excel-tab')
    for row in reader:
        print(row)

用方言名的前提是你知道文件对应哪个已注册方言。如果文件来自某个特定程序但不在已注册列表里,还是回到 Sniffer 或手动指定参数。

读取报错时怎么定位到具体行

如果文件内容不符合 CSV 格式,读取时可能抛出 csv.Error。reader 对象的 line_num 属性记录从源迭代器读取的行数,注意它和返回的记录数不是一回事,因为一条记录可能跨多行。

下面是一个自建示例,演示怎么在报错时报告文件名和行号:

import csv, sys

filename = 'exercise/sample_bad.csv'
with open(filename, newline='') as f:
    reader = csv.reader(f)
    try:
        for row in reader:
            print(row)
    except csv.Error as e:
        sys.exit(f'file {filename}, line {reader.line_num}: {e}')

这段代码只读取自建练习文件,不修改任何内容。报错信息里的行号可以帮你回到文件里查看那一行,判断是分隔符问题、引号问题还是其他格式问题。

需要说明的是,strict 参数默认为 False,即不对坏输入抛错。如果你希望遇到坏输入就报错,可以在创建 reader 时把 strict 设为 True。本文不展开 strict 的用法,只说明它的默认行为。

识别方言时的限制和容易踩的坑

第一,Sniffer 的推断是启发式的,不是权威判定。样本太短、内容太规整或分隔符出现次数太少时,推断可能出错。所以推断之后一定要用读取结果做检查。

第二,多个分隔符效果相同时,Sniffer 按 preferred 列表的顺序选择,而不是按出现次数。这意味着如果逗号和分号都能拆分样本,它会选逗号。如果你知道文件实际用分号,最好手动指定 delimiter,不要依赖推断。

第三,reader 被硬编码为识别 \r 或 \n 作为行尾,并忽略 lineterminator。所以 lineterminator 参数对读取没有影响,它只用于 writer 生成输出。

第四,本文只涉及识别方言和读取检查这些基础操作。注册自定义方言、修改 preferred 列表、处理引号字符等更细的配置不在本文范围内。如果你的文件涉及这些情况,请查阅 csv 模块文档的 Dialects and formatting parameters 一节。

最后提醒:所有操作都针对自建练习文件,操作前先备份。本文不涉及删除文件或修改系统设置。

观看知识要点视频 18 秒

18秒无旁白要点导览,文字来自已审核正文;完整步骤、条件及参考资料见下方。

参考资料

以下原始资料提供本文依据;请结合日期、适用条件和产品说明查阅。

请结合原始资料的适用条件阅读。

返回表格与数据 →