2012-07-03 4 views
18

Mon programme a besoin de lire les fichiers csv qui peuvent avoir 1,2 ou 3 colonnes, et il doit modifier son comportement en conséquence. Existe-t-il un moyen simple de vérifier le nombre de colonnes sans "consommer" une ligne avant l'exécution de l'itérateur? Le code suivant est le plus élégant que je pouvais gérer, mais je préférerais courir le chèque avant que la boucle commence:Trouver le nombre de colonnes dans le fichier csv

import csv 
f = 'testfile.csv' 
d = '\t' 

reader = csv.reader(f,delimiter=d) 
for row in reader: 
    if reader.line_num == 1: fields = len(row) 
    if len(row) != fields: 
     raise CSVError("Number of fields should be %s: %s" % (fields,str(row))) 
    if fields == 1: 
     pass 
    elif fields == 2: 
     pass 
    elif fields == 3: 
     pass 
    else: 
     raise CSVError("Too many columns in input file.") 

Edit: j'ai inclus plus d'informations sur mes données. S'il n'y a qu'un seul champ, il doit contenir un nom en notation scientifique. S'il y a deux champs, le premier doit contenir un nom et le second un code de liaison. S'il existe trois champs, le champ supplémentaire contient un indicateur qui spécifie si le nom est actuellement valide. Donc si une ligne a 1, 2 ou 3 colonnes, toutes doivent avoir la même chose.

+0

mieux utiliser 'fields' au lieu de' rows' . –

+0

@MarcodeWit merci, il devrait bien sûr être 'fields'! Je suis également d'accord avec votre point de vue que toutes les lignes n'ont pas nécessairement le même nombre de champs. Dans ce cas, j'ai décidé qu'il devrait déclencher une exception si une telle ligne est rencontrée. – rudivonstaden

Répondre

18

Vous pouvez utiliser itertools.tee

itertools.tee (itérables [, n = 2])
Retour n itérateurs indépendants à partir d'un seul itérables.

par ex.

reader1, reader2 = itertools.tee(csv.reader(f, delimiter=d)) 
columns = len(next(reader1)) 
del reader1 
for row in reader2: 
    ... 

Notez qu'il est important de supprimer la référence à reader1 lorsque vous avez terminé avec elle - sinon tee devra stocker toutes les lignes en mémoire au cas où vous appelez next(reader1) à nouveau

4

Que se passe si la l'utilisateur vous fournit un fichier CSV avec moins de colonnes? Les valeurs par défaut sont-elles utilisées à la place?

Si oui, pourquoi ne pas étendre la ligne avec des valeurs nulles à la place?

reader = csv.reader(f,delimiter=d) 
for row in reader: 
    row += [None] * (3 - len(row)) 
    try: 
     foo, bar, baz = row 
    except ValueError: 
     # Too many values to unpack: too many columns in the CSV 
     raise CSVError("Too many columns in input file.") 

maintenant bar et baz sera au moins None et le gestionnaire d'exception se chargera de toutes les lignes de plus de 3 articles.

+0

Le problème avec cette approche est que chaque ligne doit avoir systématiquement les mêmes données dans chaque champ, mais les données sont difficiles à valider. – rudivonstaden

+0

Je ne vois pas comment mon approche impose plus de contraintes de consistance que de tester la longueur de la première ligne. D'ailleurs, vous ne nous avez pas beaucoup parlé de vos données; par conséquent, ma réponse commence par une question. –

+0

vous avez raison, j'ai édité ma question pour en inclure plus sur les données, et aussi pour inclure une vérification de cohérence par ligne. – rudivonstaden

7

Cela semble fonctionner aussi bien:

import csv 

datafilename = 'testfile.csv' 
d = '\t' 
f=open(datafilename,'r') 

reader=csv.reader(f,delimiter=d) 
ncol=len(next(reader)) # Read first line and count columns 
f.seek(0)    # go back to beginning of file 
for row in reader: 
    pass #dostuff 
-1

Je rebâtirait comme suit (si le fichier est pas trop grand):

import csv 
f = 'testfile.csv' 
d = '\t' 

reader = list(csv.reader(f,delimiter=d)) 
fields = len(reader[0]) 
for row in reader: 
    if fields == 1: 
     pass 
    elif fields == 2: 
     pass 
    elif fields == 3: 
     pass 
    else: 
     raise CSVError("Too many columns in input file.") 
+0

@Martijn Pieters: C'est pourquoi j'ai commencé avec l'avertissement sur la taille du fichier. La fonction 'list' copie tout depuis l'itérateur, donc seuls les petits fichiers csv conviennent. –

+0

Donc, vous le faites, en effet. –

+0

Je ne reconstruirais pas le code si le fichier est trop gros. –

Questions connexes