如何从流中读取CSV文件并处理每行数据?

45 投票
3 回答
52451 浏览
提问于 2025-04-16 20:42

我想从标准输入读取一个CSV文件,并且在每一行到达时就处理它。我的CSV输出代码是逐行写入的,但我的读取代码却要等到整个流结束后才开始处理这些行。这是csv模块的限制吗?还是我做错了什么?

我的读取代码:

import csv
import sys
import time


reader = csv.reader(sys.stdin)
for row in reader:
    print "Read: (%s) %r" % (time.time(), row)

我的写入代码:

import csv
import sys
import time


writer = csv.writer(sys.stdout)
for i in range(8):
    writer.writerow(["R%d" % i, "$" * (i+1)])
    sys.stdout.flush()
    time.sleep(0.5)

运行python test_writer.py | python test_reader.py的输出结果:

Read: (1309597426.3) ['R0', '$']
Read: (1309597426.3) ['R1', '$$']
Read: (1309597426.3) ['R2', '$$$']
Read: (1309597426.3) ['R3', '$$$$']
Read: (1309597426.3) ['R4', '$$$$$']
Read: (1309597426.3) ['R5', '$$$$$$']
Read: (1309597426.3) ['R6', '$$$$$$$']
Read: (1309597426.3) ['R7', '$$$$$$$$']

你可以看到所有的打印语句都是同时执行的,但我本来希望它们之间有500毫秒的间隔。

3 个回答

0

你在清空标准输出(stdout),但没有清空标准输入(stdin)。

Sys.stdin 也有一个 flush() 方法,如果你真的想要关闭缓冲区,可以在每次读取一行后试着使用这个方法。

1

这可能是一个限制。你可以看看这个链接 http://docs.python.org/using/cmdline.html#cmdoption-unittest-discover-u

注意,file.readlines() 和文件对象(比如 for line in sys.stdin)内部有缓冲,这个选项不会影响它们。为了绕过这个问题,你可以在一个 while 1: 循环里使用 file.readline()。

我对 test_reader.py 做了如下修改:

import csv, sys, time

while True:
    print "Read: (%s) %r" % (time.time(), sys.stdin.readline())

输出结果

python test_writer.py | python  test_reader.py
Read: (1309600865.84) 'R0,$\r\n'
Read: (1309600865.84) 'R1,$$\r\n'
Read: (1309600866.34) 'R2,$$$\r\n'
Read: (1309600866.84) 'R3,$$$$\r\n'
Read: (1309600867.34) 'R4,$$$$$\r\n'
Read: (1309600867.84) 'R5,$$$$$$\r\n'
Read: (1309600868.34) 'R6,$$$$$$$\r\n'
Read: (1309600868.84) 'R7,$$$$$$$$\r\n'
52

正如文档中所说

为了让for循环在读取文件的每一行时效率最高(这是一个非常常见的操作),next()方法使用了一个隐藏的预读缓冲区。

你可以通过查看csv模块的实现(第784行)来看到,csv.reader调用了底层迭代器的next()方法(通过PyIter_Next)。

所以如果你真的想要对CSV文件进行无缓冲读取,你需要把文件对象(这里是sys.stdin)转换成一个迭代器,这个迭代器的next()方法实际上调用readline()。这可以很简单地通过使用iter函数的两个参数形式来完成。所以把test_reader.py中的代码改成类似下面的样子:

for row in csv.reader(iter(sys.stdin.readline, '')):
    print("Read: ({}) {!r}".format(time.time(), row))

例如,

$ python test_writer.py | python test_reader.py
Read: (1388776652.964925) ['R0', '$']
Read: (1388776653.466134) ['R1', '$$']
Read: (1388776653.967327) ['R2', '$$$']
Read: (1388776654.468532) ['R3', '$$$$']
[etc]

你能解释一下为什么需要对CSV文件进行无缓冲读取吗?也许有更好的解决方案来处理你想做的事情。

撰写回答