读取csv数据时pythoncode中出现StopIteration错误
我正在写一个程序来读取csv文件。我已将一个reader对象放入框架中,对其调用next()将得到标题行。但当我再次调用它时,它会给出StopIteration错误,尽管csv文件中有行。我正在执行file.seek(0),那么它工作正常。有人能给我解释一下吗?代码快照如下所示:读取csv数据时pythoncode中出现StopIteration错误,python,csv,iterator,Python,Csv,Iterator,我正在写一个程序来读取csv文件。我已将一个reader对象放入框架中,对其调用next()将得到标题行。但当我再次调用它时,它会给出StopIteration错误,尽管csv文件中有行。我正在执行file.seek(0),那么它工作正常。有人能给我解释一下吗?代码快照如下所示: with open(file,'r') as f: reader = csv.reader(f) header = next(reader) result = [] for co
with open(file,'r') as f:
reader = csv.reader(f)
header = next(reader)
result = []
for colname in header[2:]:
col_index = header.index(colname)
# f.seek(0)
next(reader)
您为每一列(前两列除外)调用一次
next
。所以,如果你有,比如说,10列,它将尝试读取8行
如果您有20行,这不会引发异常,但是您将忽略最后12行,这可能是您不想要的。另一方面,如果您只有5行,那么当您试图读取第6行时,它将升高
f.seek(0)
阻止异常的原因是它会在每次next
之前将文件重置回起始位置,因此您只需反复读取标题行,而忽略文件中的所有其他内容。它没有提出任何问题,但没有任何用处
您可能想要的是这样的:
with open(file,'r') as f:
reader = csv.reader(f)
header = next(reader)
result = []
for row in reader:
for col_index, colname in enumerate(header)[2:]:
value = row[col_index]
result.append(do_something_with(value, colname))
它只读取每一行一次,并对每一列执行某些操作,但每一行的前两列除外
从注释中,您实际上要做的是找到每列的最大值。因此,您确实需要迭代列,然后,在每个列中,您需要迭代行
csv.reader
是迭代器,这意味着您只能对其进行一次迭代。因此,如果你只是以显而易见的方式来做,它将不起作用:
maxes = {}
with open(file) as f:
reader = csv.reader(f)
header = next(reader)
for col_index, colname in enumerate(header)[2:]:
maxes[colname] = max(reader, key=operator.itemgetter(col_index))
第一列将在读取标题后读取剩余的内容,这很好。下一列将在读取整个文件后读取剩余的内容,而这并不是什么
那么,你如何解决这个问题呢 一种方法是每次通过外部循环重新创建迭代器:
maxes = {}
with open(file) as f:
reader = csv.reader(f)
header = next(reader)
for col_index, colname in enumerate(header)[2:]:
with open(file) as f:
reader = csv.reader(f)
next(reader)
maxes[colname] = max(reader, key=lambda row: float(row[col_index]))
这样做的问题是,您要读取整个文件N次,而从磁盘读取文件可能是程序执行的最慢的操作
您试图用
f.seek(0)
执行的操作取决于文件对象和csv.reader
对象的工作方式。虽然文件对象是迭代器,但它们是特殊的,因为它们有一种方法可以将它们重置为起始位置(或者保存一个位置并稍后返回)。和csv.reader
对象基本上是围绕文件对象的简单包装器,因此如果重置文件,也会重置读取器。(目前尚不清楚这是否能保证有效,但如果您知道csv
是如何工作的,您可能会说服自己在实践中它是安全的。)因此:
这样可以节省每次关闭和打开文件的成本,但这不是昂贵的部分;你还在一遍又一遍地读磁盘。现在,任何阅读您的代码的人都必须了解将文件对象用作迭代器但重置它们的诀窍,否则他们将不知道您的代码是如何工作的
那么,你如何避免这种情况呢 通常,当您需要在迭代器上进行多次传递时,有两个选项。简单的解决方案是将迭代器复制到可重用的iterable中,如列表:
maxes = {}
with open(file) as f:
reader = csv.reader(f)
header = next(reader)
rows = list(reader)
for col_index, colname in enumerate(header)[2:]:
maxes[colname] = max(rows, key=lambda row: float(row[col_index]))
这不仅比以前的代码简单得多,而且速度也快得多。除非文件很大。通过将所有行存储在一个列表中,您可以将整个文件一次读入内存。如果它太大而无法安装,您的程序将失败。或者,更糟糕的是,如果它合适,但仅通过使用虚拟内存,则每次循环时,程序都会将部分内存调进或调出内存,从而使交换文件颠簸,使一切都变得缓慢
另一种选择是重新组织事情,这样你只需要通过一次。这意味着您必须将循环放在外部的行上,将循环放在内部的列上。这需要重新考虑一下设计,这意味着您不能只使用简单的
max
函数,但折衷可能是值得的:
with open(file) as f:
reader = csv.reader(f)
header = next(reader)
maxes = {colname: float('-inf') for colname in header[2:]}
for row in reader:
for col_index, colname in enumerate(header)[2:]:
maxes[colname] = max(maxes[colname], float(row[col_index]))
您可以进一步简化此操作,例如,使用计数器代替普通的读写器,使用读写器代替普通的读写器,但它已经很简单、可读且高效了。您为什么不写:
header = next(reader)
最后一行也是吗?我不知道这是否是您的问题,但我将从这里开始。是的,这与OSA有关吗作为旁注,您永远不应该对条形图中的foo执行:index=bar.index(foo)
。这是缓慢的、复杂的,并且可能有问题(如果两个列具有相同的名称会发生什么情况?)。只需对enumerate(bar)中的索引foo执行。
。嗨,Abarnert,谢谢你的回复。我要做的是为每一列找到最大值。所以我对每一列都使用max()生成器表达式。你能给我一些暗示吗code@maverick:同时,在将来,请给出一个更完整的示例,说明您实际试图做的事情,并提供足够的输入,以便我们重现问题。非常感谢。我将在将来更精确地说明问题
header = next(reader)