2012-07-03 11 views
18

Il mio programma deve leggere i file csv che possono avere 1,2 o 3 colonne e deve modificare il suo comportamento di conseguenza. C'è un modo semplice per controllare il numero di colonne senza "consumare" una riga prima dell'esecuzione dell'iteratore? Il codice seguente è la più elegante ho potuto gestire, ma preferirei per eseguire il controllo prima che il ciclo for inizia:Trova il numero di colonne nel file 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: avrei incluso ulteriori informazioni sui miei dati. Se c'è un solo campo, deve contenere un nome in notazione scientifica. Se ci sono due campi, il primo deve contenere un nome e il secondo un codice di collegamento. Se ci sono tre campi, il campo aggiuntivo contiene un flag che specifica se il nome è attualmente valido. Pertanto, se una riga ha 1, 2 o 3 colonne, tutte devono avere lo stesso valore.

+0

migliore utilizzo 'fields' invece di' rows' . –

+0

@MarcodeWit grazie, dovrebbe essere ovviamente 'fields'! Ho anche concordato con il punto che non tutte le righe hanno necessariamente lo stesso numero di campi. In questo caso ho deciso che dovrebbe sollevare un'eccezione se viene rilevata una riga di questo tipo. – rudivonstaden

risposta

18

È possibile utilizzare itertools.tee

itertools.tee (iterable [, n = 2])
ritorno n iteratori indipendenti da un singolo iterabile .

es.

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

Nota che è importante per eliminare il riferimento alla reader1 quando si è finito con esso - altrimenti tee dovranno memorizzare tutti i file in memoria nel caso in cui mai chiamare next(reader1) nuovo

4

Cosa succede se il l'utente ti fornisce un file CSV con meno colonne? Vengono invece utilizzati i valori predefiniti?

In tal caso, perché non estendere la riga con valori nulli?

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.") 

Ora bar e baz sarà almeno essere None e il gestore di eccezioni si prenderà cura di tutte le righe di lunghezza superiore a 3 elementi.

+0

Il problema con questo approccio è che ogni riga deve avere costantemente gli stessi dati in ogni campo, ma i dati sono difficili da validare. – rudivonstaden

+0

Non vedo come il mio approccio ponga più limiti di coerenza rispetto al solo test della lunghezza della prima riga. Inoltre, non ci hai detto molto sui tuoi dati; quindi la mia risposta inizia con una domanda. –

+0

hai ragione, ho modificato la mia domanda per includere ulteriori informazioni sui dati e anche per includere un controllo di coerenza per riga. – rudivonstaden

7

Questo sembra funzionare così:

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

mi avrebbe ricostruito come segue (se il file non è troppo grande):

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: Ecco perché ho iniziato con l'avvertimento su filesize. La funzione 'list' copia tutto dall'iteratore, quindi sono adatti solo i file csv di piccole dimensioni. –

+0

Così, davvero. –

+0

Non vorrei ricostruire il codice se il file è troppo grande. –

Problemi correlati