web-dev-qa-db-fra.com

Comment lire un fichier CSV à partir d'un flux et traiter chaque ligne telle qu'elle est écrite?

Je voudrais lire un fichier CSV à partir de l'entrée standard et traiter chaque ligne au fur et à mesure. Mon code de sortie CSV écrit les lignes une par une, mais mon lecteur attend la fin du flux avant d'itérer les lignes. Est-ce une limitation du module csv? Est-ce que je fais quelque chose de mal?

Mon code lecteur:

import csv
import sys
import time


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

Mon code d'écrivain:

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)

Sortie de 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', '$$$$$$$$']

Comme vous pouvez le voir, toutes les instructions d'impression sont exécutées en même temps, mais je m'attends à un écart de 500 ms.

34
muhuk

Comme il dit dans la documentation ,

Afin de faire d'une boucle for le moyen le plus efficace de boucler sur les lignes d'un fichier (une opération très courante), la méthode next() utilise un tampon de lecture anticipée caché.

Et vous pouvez voir en regardant l'implémentation du module csv (ligne 784) que csv.reader Appelle la méthode next() de l'itérateur sous-jacent (via PyIter_Next).

Donc, si vous voulez vraiment une lecture sans tampon des fichiers CSV, vous devez convertir l'objet fichier (ici sys.stdin) En un itérateur dont la méthode next() appelle en fait readline() à la place. Cela peut facilement être fait en utilisant la forme à deux arguments de la fonction iter . Modifiez donc le code dans test_reader.py En quelque chose comme ceci:

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

Par exemple,

$ 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]

Pouvez-vous expliquer pourquoi vous avez besoin d'une lecture sans tampon des fichiers CSV? Il pourrait y avoir une meilleure solution à tout ce que vous essayez de faire.

39
Gareth Rees

C'est peut-être une limitation. Lisez ceci http://docs.python.org/using/cmdline.html#cmdoption-unittest-discover-

Notez qu'il existe un tampon interne dans file.readlines () et File Objects (pour la ligne dans sys.stdin) qui n'est pas influencé par cette option. Pour contourner ce problème, vous souhaiterez utiliser file.readline () dans une boucle while 1 :.

J'ai modifié test_reader.py comme suit:

import csv, sys, time

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

Production

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'
1
user

Vous videz stdout, mais pas stdin.

Sys.stdin A également une méthode flush(), essayez de l'utiliser après chaque ligne lue si vous voulez vraiment désactiver la mise en mémoire tampon.

0
RoundTower