Как прочитать файл CSV из потока и обработать каждую строку так, как она написана?

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

Решение

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

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

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

Обратите внимание, что в 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())

Выход

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'

Вы очищаете стандартный вывод, но не вводите.

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

Другие вопросы по тегам