在更新文件时使用Python csv模块
我正在使用Python的csv模块,从一个不断被外部工具更新的csv文件中提取数据。遇到的问题是,当我到达文件末尾时,会出现一个StopIteration错误,但我希望脚本能继续循环,等待外部工具添加更多行数据。
我目前想到的解决办法是:
f = open('file.csv')
csvReader = csv.reader(f, delimiter=',')
while 1:
try:
doStuff(csvReader.next())
except StopIteration:
depth = f.tell()
f.close()
f = open('file.csv')
f.seek(depth)
csvReader = csv.reader(f, delimiter=',')
这个方法能达到我想要的效果,但看起来也很糟糕。在捕获到StopIteration后继续循环是不可能的,因为一旦抛出StopIteration,之后每次调用next()时都会抛出这个错误。有没有人能给我一些建议,如何实现这个功能,让我不需要做这种笨拙的处理?或者有没有其他的Python模块可以轻松支持这个功能。
3 个回答
你很少需要明确地捕捉 StopIteration
。你可以这样做:
for row in csvReader:
doStuff(row)
至于如何检测文件中是否有新行被写入,你可以 使用 写一段 Python 代码来实现 tail -f
命令,或者tail -f
的功能。(其实这并不复杂;它基本上就是每秒检查一次文件,看文件有没有变化。这是 tail
的 C 语言源代码。)
编辑:令人失望的是,在 Python 2.x 中,使用 tail -f
的方法并没有按我预期的那样工作。看起来,读取文件的每一行是通过 fread
和一个比较大的缓冲区来实现的,即使文件应该是无缓冲的(比如当 subprocess.py 创建文件时,传入 bufsize=0)。不过,使用 tail
的方法无论如何都是一种稍微有点丑陋的解决方案。
生产者-消费者的问题可能会有点复杂。那我们不如试试使用查找和读取字节的方法呢?或者用一个命名管道怎么样?
其实,为什么不通过本地套接字来进行通信呢?
你的问题其实不是出在CSV读取器上,而是出在文件对象本身。虽然你可能还是需要像上面代码片段那样做一些复杂的操作,但更好的方法是创建一个文件对象的包装器或者子类,这样可以帮你处理这些事情,然后再用这个包装器去配合你的CSV读取器。这样可以把复杂的部分和你的CSV处理代码分开。
比如说(注意:这段代码没有经过测试):
class ReopeningFile(object):
def __init__(self, filename):
self.filename = filename
self.f = open(self.filename)
def next(self):
try:
self.f.next()
except StopIteration:
depth = self.f.tell()
self.f.close()
self.f = open(self.filename)
self.f.seek(depth)
# May need to sleep here to allow more data to come in
# Also may need a way to signal a real StopIteration
self.next()
def __iter__(self):
return self
这样你的主要代码就会变得简单,因为不需要再管理文件的重新打开(注意,当文件重新打开时,你也不需要重新启动你的csv_reader):
import csv
csv_reader = csv.reader(ReopeningFile('data.csv'))
for each in csv_reader:
process_csv_line(each)