在更新文件时使用Python csv模块

4 投票
3 回答
2147 浏览
提问于 2025-04-15 17:37

我正在使用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 个回答

0

你很少需要明确地捕捉 StopIteration。你可以这样做:

for row in csvReader:
    doStuff(row)

至于如何检测文件中是否有新行被写入,你可以 使用 tail -f 命令,或者 写一段 Python 代码来实现 tail -f 的功能。(其实这并不复杂;它基本上就是每秒检查一次文件,看文件有没有变化。这是 tail 的 C 语言源代码。

编辑:令人失望的是,在 Python 2.x 中,使用 tail -f 的方法并没有按我预期的那样工作。看起来,读取文件的每一行是通过 fread 和一个比较大的缓冲区来实现的,即使文件应该是无缓冲的(比如当 subprocess.py 创建文件时,传入 bufsize=0)。不过,使用 tail 的方法无论如何都是一种稍微有点丑陋的解决方案。

2

生产者-消费者的问题可能会有点复杂。那我们不如试试使用查找和读取字节的方法呢?或者用一个命名管道怎么样?

其实,为什么不通过本地套接字来进行通信呢?

4

你的问题其实不是出在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)

撰写回答