2010-05-23 35 views
18

J'ai un objet csv DictReader (en utilisant Python 3.1), mais je voudrais savoir le nombre de lignes/lignes contenues dans le lecteur avant Je itérer à travers elle. Quelque chose comme ce qui suit ...Nombre de lignes dans csv.DictReader

myreader = csv.DictReader(open('myFile.csv', newline='')) 

totalrows = ? 

rowcount = 0 
for row in myreader: 
    rowcount +=1 
    print("Row %d/%d" % (rowcount,totalrows)) 

Je sais que je pourrais obtenir le total en parcourant le lecteur, mais je ne pouvais pas courir la boucle « pour ». Je pourrais parcourir une copie du lecteur, mais je ne trouve pas comment copier un itérateur.

Je pourrais aussi utiliser

totalrows = len(open('myFile.csv').readlines()) 

mais qui semble une réouverture inutile du dossier. Je préférerais obtenir le compte du DictReader si possible.

Toute aide serait appréciée.

Alan

Répondre

22
rows = list(myreader) 
totalrows = len(rows) 
for i, row in enumerate(rows): 
    print("Row %d/%d" % (i+1, totalrows)) 
+0

Bonne solution - Je suis assez nouveau à l'idée des itérateurs, donc je n'avais pas vraiment apprécié énumerate() jusqu'à présent. Cordialement. –

+7

Faites juste attention à la taille de vos données ici. Transformer votre lecteur en une liste pourrait prendre des GOBS de la mémoire. –

+1

Cela va charger toutes les données en mémoire, compter les lignes -1 est beaucoup de bonnes solutions –

2

Je ne peux pas trouver comment copier un iterator.

le plus proche est itertools.tee, mais simplement faire un list de celui-ci, comme @JFSebastian suggère, est le mieux ici, comme docs de itertools.tee expliquent:

Cette itertool peut nécessiter importante stockage auxiliaire (en fonction de combien beaucoup de données temporaires doivent être stockées ). En général, si un itérateur utilise la plupart ou la totalité des données avant un autre itérateur démarre, il est plus rapide d'utiliser pour utiliser list() au lieu de tee().

+0

Vous avez toujours la consommation de ressources potentiellement massive avec les deux méthodes. –

+0

Merci Alex - liste c'est alors. –

12

Il vous suffit d'ouvrir le fichier une fois:

import csv 

f = open('myFile.csv', 'rb') 

countrdr = csv.DictReader(f) 
totalrows = 0 
for row in countrdr: 
    totalrows += 1 

f.seek(0) # You may not have to do this, I didn't check to see if DictReader did 

myreader = csv.DictReader(f) 
for row in myreader: 
    do_work 

Peu importe ce que vous faites, vous devez faire deux passes (bien, si vos dossiers sont une longueur fixe - ce qui est peu probable - vous pourriez il suffit d'obtenir la taille du fichier et de diviser, mais supposons que ce n'est pas le cas). Ouvrir à nouveau le fichier ne vous coûte vraiment pas cher, mais vous pouvez l'éviter comme illustré ici. La conversion en une liste juste pour utiliser len() risque de gaspiller des tonnes de mémoire et de ne pas être plus rapide.

Note: Le « Pythonic » est d'utiliser enumerate au lieu de +=, mais le UNPACK_TUPLE opcode est si cher qu'il fait enumerate plus lent que incrémenter un local. Cela étant dit, il s'agit probablement d'une micro-optimisation inutile que vous devriez probablement éviter.

Plus de remarques: Si vous voulez vraiment générer un type d'indicateur de progression, il ne doit pas forcément être basé sur des enregistrements. Vous pouvez tell() sur l'objet de fichier dans la boucle et simplement signaler quel% des données que vous avez. Ça va être un peu irrégulier, mais il y a des chances que sur n'importe quel fichier qui soit suffisamment grand pour justifier une barre de progression, l'écart sur la longueur d'enregistrement sera perdu dans le bruit.

+0

Nick - merci pour la réponse. On dirait que mon refus de rouvrir le fichier ne vaut pas le code supplémentaire impliqué (la lisibilité est supérieure à la performance dans ce cas). Merci pour le conseil concernant la vitesse d'énumération(). Tell() est aussi nouveau pour moi - j'y reviendrai plus tard. Cordialement. –

+0

Seul problème avec ceci .. que se passe-t-il si vous utilisez une vapeur. – Nick

+0

@Nick: Il n'y a pas de magie dans le monde - ce n'est pas un problème, juste un fait. –