csv — Archivos de valores separados por comas

Propósito:Lee y escribe archivos de valores separados por comas.

El módulo csv se puede usar para trabajar con datos exportados desde hojas de cálculo y bases de datos en archivos de texto formateados con campos y registros, comúnmente conocidos como formato de valores separados por comas (CSV) porque las comas se usan a menudo para separar los campos en un registro.

Leer

Usa reader() para crear un objeto para leer datos de un archivo CSV. El lector se puede utilizar como un iterador para procesar las filas de El archivo en orden. Por ejemplo

csv_reader.py
import csv
import sys

with open(sys.argv[1], 'rt') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

El primer argumento de reader() es la fuente de las líneas de texto. En este caso, es un archivo, pero se acepta cualquier iterable (una instancia StringIO, list, etc.). Otros argumentos opcionales pueden ser dados a controlar cómo se analizan los datos de entrada.

"Title 1","Title 2","Title 3","Title 4"
1,"a",08/18/07,"å"
2,"b",08/19/07,"∫"
3,"c",08/20/07,"ç"

A medida que se lee, cada fila de los datos de entrada se analiza y se convierte en una list de cadenas de texto.

$ python3 csv_reader.py testdata.csv

['Title 1', 'Title 2', 'Title 3', 'Title 4']
['1', 'a', '08/18/07', 'å']
['2', 'b', '08/19/07', '∫']
['3', 'c', '08/20/07', 'ç']

El analizador maneja saltos de línea incrustados dentro de cadenas en una fila, por lo que una «fila» no es siempre lo mismo que una «línea» de entrada del archivo.

"Title 1","Title 2","Title 3"
1,"first line
second line",08/18/07

Los campos con saltos de línea en la entrada conservan los saltos de línea internos cuando son devueltos por el analizador.

$ python3 csv_reader.py testlinebreak.csv

['Title 1', 'Title 2', 'Title 3']
['1', 'first line\nsecond line', '08/18/07']

Escribir

Escribir archivos CSV es tan fácil como leerlos. Utiliza writer() para crear un objeto para escribir, luego itera sobre las filas, usando writerow() para imprimirlos.

csv_writer.py
import csv
import sys

unicode_chars = 'å∫ç'

with open(sys.argv[1], 'wt') as f:
    writer = csv.writer(f)
    writer.writerow(('Title 1', 'Title 2', 'Title 3', 'Title 4'))
    for i in range(3):
        row = (
            i + 1,
            chr(ord('a') + i),
            '08/{:02d}/07'.format(i + 1),
            unicode_chars[i],
        )
        writer.writerow(row)

print(open(sys.argv[1], 'rt').read())

La salida no se ve exactamente como los datos exportados utilizados en el ejemplo del lector porque éste carece de comillas alrededor de algunos de los valores.

$ python3 csv_writer.py testout.csv

Title 1,Title 2,Title 3,Title 4
1,a,08/01/07,å
2,b,08/02/07,∫
3,c,08/03/07,ç

Entrecomillar

El comportamiento de entrecomillado predeterminado es diferente para el escritor, por lo que la segunda y tercera columnas en el ejemplo anterior no se citan. Para agregar comillas, establece el argumento quoting a una de los otras modos de comillas.

writer = csv.writer(f, quoting=csv.QUOTE_NONNUMERIC)

En este caso, QUOTE_NONNUMERIC agrega comillas a todas las columnas que contienen valores que no son números.

$ python3 csv_writer_quoted.py testout_quoted.csv

"Title 1","Title 2","Title 3","Title 4"
1,"a","08/01/07","å"
2,"b","08/02/07","∫"
3,"c","08/03/07","ç"

Hay cuatro opciones de entrecomillado diferentes, definidas como constantes en el módulo csv.

QUOTE_ALL
Entrecomilla todo, independientemente del tipo.
QUOTE_MINIMAL
Entrecomilla campos con caracteres especiales (cualquier cosa que pueda confundir a un analizador) Configurado con el mismo dialecto y opciones). Este es el valor por defecto
QUOTE_NONNUMERIC
Entrecomilla todos los campos que no sean números enteros o flotantes. Cuando se usa con el lector, los campos de entrada que no se entrecomillan se convierten en números flotantes.
QUOTE_NONE
No entrecomilla nada en la salida. Cuando se usa con el lector, caracteres entrecomilla se incluyen en los valores de campo (normalmente, se tratan como delimitadores y son eliminados).

Dialectos

No existe un estándar bien definido para archivos de valores separados por comas, por lo que el analizador necesita ser flexible. Esta flexibilidad significa que hay muchos parámetros para controlar cómo csv analiza o escribe datos. En lugar de pasar cada uno de estos parámetros al lector y escritor por separado, se agrupan en un objeto dialecto.

Las clases de dialectos se pueden registrar por nombre, de modo que las personas que llaman el módulo csv no necesitan conocer los ajustes de parámetros de antemano. La lista completa de dialectos registrados se puede recuperar con list_dialects().

csv_list_dialects.py
import csv

print(csv.list_dialects())

La biblioteca estándar incluye tres dialectos: excel, excel-tabs, y unix. El dialecto excel es para trabajar con datos en el formato de exportación predeterminado para Microsoft Excel, también trabaja con LibreOffice. El dialecto unix entrecomilla todos los campos con comillas dobles y usa \n como el separador de registros.

$ python3 csv_list_dialects.py

['excel', 'excel-tab', 'unix']

Crear un dialecto

Si, en lugar de usar comas para delimitar campos, el archivo de entrada usa pipes (|), como este

"Title 1"|"Title 2"|"Title 3"
1|"first line
second line"|08/18/07

un nuevo dialecto puede ser registrado usando el delimitador apropiado.

csv_dialect.py
import csv

csv.register_dialect('pipes', delimiter='|')

with open('testdata.pipes', 'r') as f:
    reader = csv.reader(f, dialect='pipes')
    for row in reader:
        print(row)

Usando el dialecto «pipes», el archivo se puede leer igual que con el archivo delimitado por comas.

$ python3 csv_dialect.py

['Title 1', 'Title 2', 'Title 3']
['1', 'first line\nsecond line', '08/18/07']

Parámetros de dialecto

Un dialecto especifica todos los tokens utilizados al analizar o escribir un archivo de datos. the table below enumera los aspectos del formato de archivo que pueden especificarse, desde la forma en que se delimitan las columnas hasta el carácter usado para escapar un token.

Parámetros de dialecto CSV
Atributo Valor por defecto Significado
delimiter , Separador de campo (un carácter)
doublequote True Indicador que controla si las instancias de caractéres se duplican
escapechar None Caracter usado para indicar una secuencia de escape
lineterminator \r\n Cadena utilizada por el escritor para terminar una línea
quotechar " Carácter para rodear campos que contienen valores especiales
quoting QUOTE_MINIMAL Controla el comportamiento de entrecomillado descrito anteriormente
skipinitialspace False Ignora los espacios en blanco después del delimitador de campo
csv_dialect_variations.py
import csv
import sys

csv.register_dialect('escaped',
                     escapechar='\\',
                     doublequote=False,
                     quoting=csv.QUOTE_NONE,
                     )
csv.register_dialect('singlequote',
                     quotechar="'",
                     quoting=csv.QUOTE_ALL,
                     )

quoting_modes = {
    getattr(csv, n): n
    for n in dir(csv)
    if n.startswith('QUOTE_')
}

TEMPLATE = '''\
Dialect: "{name}"

  delimiter   = {dl!r:<6}    skipinitialspace = {si!r}
  doublequote = {dq!r:<6}    quoting          = {qu}
  quotechar   = {qc!r:<6}    lineterminator   = {lt!r}
  escapechar  = {ec!r:<6}
'''

for name in sorted(csv.list_dialects()):
    dialect = csv.get_dialect(name)

    print(TEMPLATE.format(
        name=name,
        dl=dialect.delimiter,
        si=dialect.skipinitialspace,
        dq=dialect.doublequote,
        qu=quoting_modes[dialect.quoting],
        qc=dialect.quotechar,
        lt=dialect.lineterminator,
        ec=dialect.escapechar,
    ))

    writer = csv.writer(sys.stdout, dialect=dialect)
    writer.writerow(
        ('col1', 1, '10/01/2010',
         'Special chars: " \' {} to parse'.format(
             dialect.delimiter))
    )
    print()

Este programa muestra cómo aparecen los mismos datos cuando se formatean usando varios dialectos diferentes.

$ python3 csv_dialect_variations.py

Dialect: "escaped"

  delimiter   = ','       skipinitialspace = 0
  doublequote = 0         quoting          = QUOTE_NONE
  quotechar   = '"'       lineterminator   = '\r\n'
  escapechar  = '\\'

col1,1,10/01/2010,Special chars: \" ' \, to parse

Dialect: "excel"

  delimiter   = ','       skipinitialspace = 0
  doublequote = 1         quoting          = QUOTE_MINIMAL
  quotechar   = '"'       lineterminator   = '\r\n'
  escapechar  = None

col1,1,10/01/2010,"Special chars: "" ' , to parse"

Dialect: "excel-tab"

  delimiter   = '\t'      skipinitialspace = 0
  doublequote = 1         quoting          = QUOTE_MINIMAL
  quotechar   = '"'       lineterminator   = '\r\n'
  escapechar  = None

col1    1       10/01/2010      "Special chars: "" '     to parse"

Dialect: "singlequote"

  delimiter   = ','       skipinitialspace = 0
  doublequote = 1         quoting          = QUOTE_ALL
  quotechar   = "'"       lineterminator   = '\r\n'
  escapechar  = None

'col1','1','10/01/2010','Special chars: " '' , to parse'

Dialect: "unix"

  delimiter   = ','       skipinitialspace = 0
  doublequote = 1         quoting          = QUOTE_ALL
  quotechar   = '"'       lineterminator   = '\n'
  escapechar  = None

"col1","1","10/01/2010","Special chars: "" ' , to parse"

Detectar dialectos automáticamente

La mejor manera de configurar un dialecto para analizar un archivo de entrada es conocer la configuración correcta de antemano. Para datos donde los parámetros del dialecto son desconocidos, la clase Sniffer puede usarse para hacer una conjetura educada. El método sniff() toma una muestra de los datos de entrada y un argumento opcional que da el posible carácter delimitador.

csv_dialect_sniffer.py
import csv
from io import StringIO
import textwrap

csv.register_dialect('escaped',
                     escapechar='\\',
                     doublequote=False,
                     quoting=csv.QUOTE_NONE)
csv.register_dialect('singlequote',
                     quotechar="'",
                     quoting=csv.QUOTE_ALL)

# Generate sample data for all known dialects
samples = []
for name in sorted(csv.list_dialects()):
    buffer = StringIO()
    dialect = csv.get_dialect(name)
    writer = csv.writer(buffer, dialect=dialect)
    writer.writerow(
        ('col1', 1, '10/01/2010',
         'Special chars " \' {} to parse'.format(
             dialect.delimiter))
    )
    samples.append((name, dialect, buffer.getvalue()))

# Guess the dialect for a given sample, and then use the results
# to parse the data.
sniffer = csv.Sniffer()
for name, expected, sample in samples:
    print('Dialect: "{}"'.format(name))
    print('In: {}'.format(sample.rstrip()))
    dialect = sniffer.sniff(sample, delimiters=',\t')
    reader = csv.reader(StringIO(sample), dialect=dialect)
    print('Parsed:\n  {}\n'.format(
          '\n  '.join(repr(r) for r in next(reader))))

sniff() devuelve una instancia Dialect con la configuración a ser utilizada para analizar los datos. Los resultados no son siempre perfectos, como se demuestra por el dialecto «escaped» en el ejemplo.

$ python3 csv_dialect_sniffer.py

Dialect: "escaped"
In: col1,1,10/01/2010,Special chars \" ' \, to parse
Parsed:
  'col1'
  '1'
  '10/01/2010'
  'Special chars \\" \' \\'
  ' to parse'

Dialect: "excel"
In: col1,1,10/01/2010,"Special chars "" ' , to parse"
Parsed:
  'col1'
  '1'
  '10/01/2010'
  'Special chars " \' , to parse'

Dialect: "excel-tab"
In: col1        1       10/01/2010      "Special chars "" '      to parse"
Parsed:
  'col1'
  '1'
  '10/01/2010'
  'Special chars " \' \t to parse'

Dialect: "singlequote"
In: 'col1','1','10/01/2010','Special chars " '' , to parse'
Parsed:
  'col1'
  '1'
  '10/01/2010'
  'Special chars " \' , to parse'

Dialect: "unix"
In: "col1","1","10/01/2010","Special chars "" ' , to parse"
Parsed:
  'col1'
  '1'
  '10/01/2010'
  'Special chars " \' , to parse'

Usar nombres de campo

Además de trabajar con secuencias de datos, el módulo csv incluye clases para trabajar con filas como diccionarios para que el los campos pueden ser nombrados. Las clases DictReader y DictWriter traducen filas a diccionarios en lugar de listas. Las llaves para la diccionario se pueden pasar, o inferir desde la primera fila en la entrada (cuando la fila contiene encabezados).

csv_dictreader.py
import csv
import sys

with open(sys.argv[1], 'rt') as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(row)

El lector y escritor basados en diccionarios se implementan como envoltorios alrededor de las clases basadas en secuencias, y usan los mismos métodos y argumentos. La única diferencia en la interfaz de programación del lector es que las filas son devueltas como diccionarios en lugar de listas o tuplas.

$ python3 csv_dictreader.py testdata.csv

{'Title 2': 'a', 'Title 3': '08/18/07', 'Title 4': 'å', 'Title 1
': '1'}
{'Title 2': 'b', 'Title 3': '08/19/07', 'Title 4': '∫', 'Title 1
': '2'}
{'Title 2': 'c', 'Title 3': '08/20/07', 'Title 4': 'ç', 'Title 1
': '3'}

Al DictWriter se le debe dar una lista de nombres de campo para que sepa como ordenar las columnas en la salida.

csv_dictwriter.py
import csv
import sys

fieldnames = ('Title 1', 'Title 2', 'Title 3', 'Title 4')
headers = {
    n: n
    for n in fieldnames
}
unicode_chars = 'å∫ç'

with open(sys.argv[1], 'wt') as f:

    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()

    for i in range(3):
        writer.writerow({
            'Title 1': i + 1,
            'Title 2': chr(ord('a') + i),
            'Title 3': '08/{:02d}/07'.format(i + 1),
            'Title 4': unicode_chars[i],
        })

print(open(sys.argv[1], 'rt').read())

Los nombres de campo no se escriben en el archivo automáticamente, pero se pueden escribir explícitamente usando el método writeheader().

$ python3 csv_dictwriter.py testout.csv

Title 1,Title 2,Title 3,Title 4
1,a,08/01/07,å
2,b,08/02/07,∫
3,c,08/03/07,ç

Ver también