Как прочитать файл CSV из потока и обработать каждую строку так, как она написана? - PullRequest
30 голосов
/ 02 июля 2011

Я хотел бы прочитать 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 ]

34 голосов
/ 02 июля 2011

Как сказано в документации ,

Чтобы сделать цикл 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-файлов?Возможно, есть лучшее решение для всего, что вы пытаетесь сделать.

1 голос
/ 02 июля 2011

Может быть, это ограничение.Прочитайте это http://docs.python.org/using/cmdline.html#cmdoption-unittest-discover-u

Обратите внимание, что существует внутренняя буферизация в file.readlines () и File Objects (для строки в sys.stdin), на которую не влияет эта опция.Чтобы обойти это, вы захотите использовать file.readline () внутри цикла while 1:.

Я изменил test_reader.py следующим образом:

import csv, sys, time

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

Output

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'
0 голосов
/ 02 июля 2011

Вы сбрасываете стандартный вывод, но не используете стандартный ввод.

Sys.stdin также имеет метод flush(), попробуйте использовать его после каждого чтения строки, если вы действительно хотите отключить буферизацию.

...