功能说明
用户上传的 CSV 很少完全统一:欧洲地区常用分号,日志导出可能用制表符,某些系统用竖线。直接按逗号切分会把整行当成一个字段。Python 标准库的 csv.Sniffer 会统计候选分隔符在样本中的出现规律,结合引号与转义特征推断方言。本例把推断结果整理成字典,并用同一个方言交给 DictReader 解析,使表头自动成为每行的键。
函数只依赖标准库,适合命令行工具、数据导入脚本和测试夹具。返回字典而不是 csv.Dialect 对象,便于打印、记录日志或在配置中复用。
关键代码
detect_dialect 调用 Sniffer().sniff,并把候选分隔符限制为逗号、分号、制表符和竖线,避免把普通文本中的字母误判为分隔符。返回的 dialect 对象提供 delimiter、quotechar、doublequote 和 skipinitialspace 四项。read_rows 再次推断方言后创建 DictReader,用 StringIO 包装字符串,最后把每一行转成普通字典,方便 JSON 序列化。main 用分号样本验证识别结果。
使用注意
Sniffer 需要足够多的样本才能可靠判断,只有一行数据时可能猜错,传入前几行即可。空文件或字段极少的内容会抛出 csv.Error,应捕获后回退到默认方言。推断只覆盖分隔符与引号,不处理编码、换行符和千位分隔。DictReader 遇到重复表头会覆盖前面的列,读取后应校验列名唯一性。自动识别不应替代用户显式指定方言,生产导入界面最好允许手动覆盖。
验证方式
运行脚本,方言字典应显示 delimiter 为分号、quotechar 为双引号,skipinitialspace 为 False。行列表包含两条记录,字段为 name、role、city,第二条城市为 New York,证明分号被正确切分。可把样本改成制表符分隔,确认 delimiter 跟随变化。
代码示例
import csv
import io
def detect_dialect(sample: str) -> dict[str, object]:
dialect = csv.Sniffer().sniff(sample, delimiters=",;\t|")
return {
"delimiter": dialect.delimiter,
"quotechar": dialect.quotechar,
"doublequote": dialect.doublequote,
"skipinitialspace": dialect.skipinitialspace,
}
def read_rows(text: str) -> list[dict[str, str]]:
dialect = csv.Sniffer().sniff(text, delimiters=",;\t|")
reader = csv.DictReader(io.StringIO(text), dialect=dialect)
return [dict(row) for row in reader]
if __name__ == "__main__":
sample = "name;role;city\nAda;engineer;London\nGrace;admiral;New York\n"
print(detect_dialect(sample))
print(read_rows(sample))