为什么csv.DictReader会跳过空行?

3

看起来 csv.DictReader 会跳过空行,即使设置了 restval。使用以下代码,输入文件中的空行将被跳过:

import csv
CSV_FIELDS = ("field1", "field2", "field3")
for row in csv.DictReader(open("f"), fieldnames=CSV_FIELDS, restval=""):
    if not row or not row[CSV_FIELDS[0]]:
        sys.exit("never reached, why?")

文件 f 的位置:

1,2,3


a,b,c

2
期望的行为是什么? - mdurant
如果你有一行只有两个逗号的空行,即 ,,,那么将打印出 "never reached, why?"。这是你想要的吗? - Bhargav Rao
就我所知,这是我所期望的行为,我甚至会声称这是显而易见的:空行不是空记录,它是没有记录。 - Konrad Rudolph
@BhargavRao:当然可以,但我想知道有多少空行。 - user3921265
1
@KonradRudolph:这可能是您所期望的,但这不是我对文档的解释。在我的情况下,我想知道何时行为空,或者换句话说,当所有值都丢失时。 - user3921265
显示剩余5条评论
3个回答

5

csv.DictReader类的内部实现:

    # unlike the basic reader, we prefer not to return blanks,
    # because we will typically wind up with a dict full of None
    # values
    while row == []:
        row = self.reader.next()

空行会被跳过。

如果您不想跳过空行,可以使用csv.reader来代替。

另一个选项是子类化csv.DictReader

import csv
CSV_FIELDS = ("field1", "field2", "field3")

class MyDictReader(csv.DictReader):
    def next(self):
        if self.line_num == 0:
            # Used only for its side effect.
            self.fieldnames
        row = self.reader.next()
        self.line_num = self.reader.line_num

        d = dict(zip(self.fieldnames, row))
        lf = len(self.fieldnames)
        lr = len(row)
        if lf < lr:
            d[self.restkey] = row[lf:]
        elif lf > lr:
            for key in self.fieldnames[lr:]:
                d[key] = self.restval
        return d

for row in MyDictReader(open("f", 'rb'), fieldnames=CSV_FIELDS, restval=""):
    print(row)

产量
{'field2': '2', 'field3': '3', 'field1': '1'}
{'field2': '', 'field3': '', 'field1': ''}
{'field2': '', 'field3': '', 'field1': ''}
{'field2': 'b', 'field3': 'c', 'field1': 'a'}

啊,太好了...从reader到DictReader的行为不同,感谢您发现了这个问题。 - user3921265
哇,这真是干净利落的代码,可以在2.6上直接运行,非常感谢!(而且您不介意回答我的问题,我觉得很酷) - user3921265

3

Unutbu已经解释了这种情况的原因,无论如何,一个快速的解决方法是在将空行传递给DictReader之前用','替换它们,然后restval会处理其余的事情。

CSV_FIELDS = ("field1", "field2", "field3")

with open('test.csv') as f:
    lines = (',' if line.isspace() else line for line in f)
    for row in csv.DictReader(lines, fieldnames=CSV_FIELDS, restval=""):
        print row

#output
{'field2': '2', 'field3': '3', 'field1': '1'}
{'field2': '', 'field3': '', 'field1': ''}
{'field2': '', 'field3': '', 'field1': ''}
{'field2': 'b', 'field3': 'c', 'field1': 'a'}

更新:

如果存在多行空值,上述代码无法处理,这种情况下,您可以像下面这样使用csv.reader

RESTVAL = ''

with open('test.csv') as f:
    for row in csv.reader(f, quotechar='"'):
        if not row:
            # Don't use `dict.fromkeys` if RESTVAL is a mutable object
            # {k: RESTVAL for k in CSV_FIELDS}
            print dict.fromkeys(CSV_FIELDS, RESTVAL)
        else:
            print {k: v if v else RESTVAL for k, v in zip(CSV_FIELDS, row)}

如果文件包含:
1,2,"


4"


a,b,c

那么输出结果将是:
{'field2': '2', 'field3': '\n\n\n4', 'field1': '1'}
{'field2': '', 'field3': '', 'field1': ''}
{'field2': '', 'field3': '', 'field1': ''}
{'field2': 'b', 'field3': 'c', 'field1': 'a'}

谢谢您的回复,但是我的真实CSV文件中有多行值。 - user3921265
@isonix 你所说的多行值是什么意思? - Ashwini Chaudhary
一个空行可能是单个值的一部分,而不仅仅是“所有值缺失的CSV空行”。 - user3921265
看起来很酷,我现在正在尝试,谢谢!为什么要使用 quotechar='"' - user3921265
@isonix 我的样本输入使用 " 来表示多行数据。 - Ashwini Chaudhary

0

这是你的文件:

1,2,3
,,
,,
a,b,c

我添加了逗号,现在它会有两个空行 {'field2': '', 'field3': '', 'field1': ''} 对于restval参数,它只是说如果你设置了字段但缺少一个,其他值将转到此值。

所以你设置了三个字段,每次都有三个值。但我们在这里谈论的是“列”,而不是“行”。

你的行是空的,所以他跳过了它,除非你用逗号指定他需要采取空值,对于dictreader。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接