2012-07-03 30 views
18

Mi programa necesita leer archivos csv que pueden tener 1,2 o 3 columnas, y debe modificar su comportamiento en consecuencia. ¿Hay una manera simple de verificar el número de columnas sin "consumir" una fila antes de que se ejecute el iterador? El siguiente código es el más elegante que pude, pero yo preferiría correr el cheque antes de que el bucle comienza:Buscar número de columnas en el archivo 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.") 

Editar: debería haber incluido más información sobre mis datos. Si solo hay un campo, debe contener un nombre en notación científica. Si hay dos campos, el primero debe contener un nombre y el segundo un código de enlace. Si hay tres campos, el campo adicional contiene un indicador que especifica si el nombre es actualmente válido. Por lo tanto, si una fila tiene 1, 2 o 3 columnas, todas deben tener lo mismo.

+0

mejor uso fields' 'en lugar de' rows' . –

+0

@MarcodeWit gracias, debería ser, por supuesto, 'fields' También estoy de acuerdo con su punto de que no todas las filas necesariamente tienen el mismo número de campos. En este caso, he decidido que debería generar una excepción si se encuentra una fila. – rudivonstaden

Respuesta

18

Puede utilizar itertools.tee

itertools.tee (iterable [, n = 2])
Volver n iteradores independientes a partir de una sola iterable.

por ejemplo.

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

Tenga en cuenta que es importante suprimir la referencia a reader1 cuando haya terminado con él - de lo contrario tee tendrá que almacenar todas las filas de la memoria en caso de que alguna vez llama next(reader1) nuevo

4

¿Qué pasa si el el usuario le proporciona un archivo CSV con menos columnas? ¿Se usan valores predeterminados en su lugar?

En caso afirmativo, ¿por qué no extender la fila con valores nulos?

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

Ahora bar y baz al menos será None y el manejador de excepción se hará cargo de todas las filas de más de 3 artículos.

+0

El problema con este enfoque es que cada fila debe tener consistentemente los mismos datos en cada campo, pero los datos son difíciles de validar. – rudivonstaden

+0

No veo cómo mi enfoque pone más restricciones de consistencia que solo probar la longitud de la primera fila. Además, no nos dijiste mucho sobre tus datos; por lo tanto, mi respuesta comienza con una pregunta. –

+0

tiene razón, edité mi pregunta para incluir más información sobre los datos y también para incluir una verificación de coherencia por fila. – rudivonstaden

7

Esto parece funcionar así:

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

que reconstruiría la siguiente manera (si el archivo no es demasiado 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: Es por eso que comencé con la advertencia sobre el tamaño del archivo. La función 'list' copia todo desde el iterador, por lo que solo pequeños archivos csv son adecuados. –

+0

Así que lo haces, de hecho. –

+0

No volvería a generar el código si el archivo es demasiado grande. –

Cuestiones relacionadas