如何用csv.DictReader跳过前导行?

15 投票
3 回答
15134 浏览
提问于 2025-04-17 03:17

我想让 csv.DictReader 从文件中自动识别字段名。文档上说:“如果不提供字段名参数,csv文件第一行的值将被用作字段名。”但是在我的情况下,第一行是标题,第二行才是字段名。

我不能像Python 3.2 跳过 csv.DictReader 中的一行那样使用 next(reader),因为字段名的分配是在初始化读取器时进行的(或者我可能做错了)。

这个csv文件是从Excel 2010导出的,原始来源

CanVec v1.1.0,,,,,,,,,^M
Entity,Attributes combination,"Specification Code
Point","Specification Code
Line","Specification Code
Area",Generic Code,Theme,"GML - Entity name
Shape - File name
Point","GML - Entity name
Shape - File name
Line","GML - Entity name
Shape - File name
Area"^M
Amusement park,Amusement park,,,2260012,2260009,LX,,,LX_2260009_2^M
Auto wrecker,Auto wrecker,,,2360012,2360009,IC,,,IC_2360009_2^M

我的代码:

f = open(entities_table,'rb')
try:
    dialect = csv.Sniffer().sniff(f.read(1024))
    f.seek(0)

    reader = csv.DictReader(f, dialect=dialect)
    print 'I think the field names are:\n%s\n' % (reader.fieldnames)

    i = 0
    for row in reader:
        if i < 20:
            print row
            i = i + 1

finally:
    f.close()

当前结果:

I think the field names are:
['CanVec v1.1.0', '', '', '', '', '', '', '', '', '']

期望结果:

I think the field names are:
['Entity','Attributes combination','"Specification Code Point"',...snip]

我意识到直接删除第一行会比较方便,但我想尽量做到直接读取数据,减少手动操作。

3 个回答

0

通用解决方案

这是一个更通用的解决方案,适用于不确定标题会出现在第几行的情况

这个方案还清理了标题,它会把第一个“单词”(用空格分开的)当作实际的列标题,后面的字符会被当作注释处理并去掉。

无法完全在 csv.DictReader 中处理

csv.DictReader 中无法处理跳过的行。首先,它没有“跳过行数”的功能(像 Pandas 那样),而且我们也不知道要跳过多少行。因此,需要进行外部处理,必须先读取文件内容并进行处理。

干净的解决方案

欢迎提出改进建议,但这个方案避免了导入不必要的库,也没有使用像 在上下文管理器中关闭文件再重新打开 这样的破坏性技巧(!)。

Python 3

import csv

FILENAME = "data.csv"


with open(FILENAME, newline="", encoding="utf-8") as csvfile:
    for line in csvfile:
        if "known_field_name" in line:
            break
    else:
        sys.exit("Error: Headers not found")
    fieldnames = [(field.strip().split() or [""])[0] for field in line.split(",")]
    rows = [row for row in csv.DictReader(csvfile, fieldnames=fieldnames)]

print("Field names are: {}".format(fieldnames)) 

for row in rows:
    print(row)

请注意,这段代码不会产生问题中预期的结果,因为测试数据中的列标题包含空格,而这个解决方案有一个特定的功能来清理列标题。

如果允许列标题中有空格,可以去掉这个功能。

一次性读取整个文件

这个方案一次性读取整个文件,以便尽早丢弃文件句柄,因为它是针对较小的数据集设计的。如果数据集很大,就应该在 csvfile 的上下文管理器中继续处理,而不是一次性把整个文件读入初始列表。

字段名称处理

(field.strip().split() or [""])[0] 这一步是用来获取干净的列标题。因为 field.strip() 可能会产生一个空字符串,所以 field.strip().split() 也可能会产生一个空列表,这样 [][0] 就会引发异常。

这里的 ... or [""] 确保有一个长度为1或更大的列表,以避免引发 IndexError

索引第一个项目是必要的,以便将列标题单元格中的第一个单词与其他注释分开(不允许有换行符或逗号,见下文)。

漏洞

字段名称的处理必须手动完成,不能在 csv 中进行。特别是,简单的按逗号分割的方法预计会从单行中提供所有列标题。

  • 不允许在列标题中使用逗号
  • 不允许在列标题中使用换行符

如果违反了这两条规则中的任何一条或两条,这段代码将无法“意识到”,处理将会中断。

19

在执行 f.seek(0) 之后,插入:

next(f)

这样可以把文件指针移动到第二行,然后再初始化 DictReader

1

我使用了itertools里的islice。我的表头在一个很长的前言的最后一行。我已经把前言传递过去,并用headerline作为字段名:

with open(file, "r") as f:
    '''Pass preamble'''
    n = 0
    for line in f.readlines():
        n += 1
        if 'same_field_name' in line: # line with field names was found
            h = line.split(',')
            break
    f.close()
    f = islice(open(i, "r"), n, None)

    reader = csv.DictReader(f, fieldnames = h)

撰写回答