Los archivos CSV siguen siendo uno de los formatos más utilizados para intercambiar datos entre hojas de cálculo, sistemas administrativos, bases de datos y scripts. Su apariencia simple puede engañar: separadores distintos, comillas, saltos de línea, codificaciones y columnas incompletas pueden provocar errores difíciles de detectar. Python incluye el módulo csv, que permite leer y escribir estos archivos sin depender de librerías externas.
Para ampliar el tema puedes consultar la guía de archivos de texto en Python, el tutorial de Pandas para principiantes, la introducción a diccionarios, el manejo de excepciones y la explicación de bucles for.
Las fuentes externas recomendadas son la documentación oficial del módulo csv y el RFC 4180 sobre el formato CSV. Aunque existen variaciones, estas referencias ayudan a entender las reglas de comillas, líneas y separadores.
Qué es realmente un archivo CSV
CSV significa valores separados por comas, pero no todos los archivos utilizan comas. En muchos países se usa punto y coma porque la coma representa decimales. Además, un campo puede contener el propio separador, comillas o saltos de línea. Por eso no conviene dividir cada fila manualmente con split(',').
nombre,ciudad,comentario
Ana,Madrid,"Prefiere correo, no teléfono"
El módulo csv interpreta correctamente las comillas y evita que el comentario se convierta en dos columnas.
Leer un CSV básico
import csv
with open("clientes.csv", "r", encoding="utf-8", newline="") as archivo:
lector = csv.reader(archivo)
for fila in lector:
print(fila)
El parámetro newline="" permite que el módulo controle correctamente los saltos de línea, especialmente en Windows. Define siempre la codificación; UTF-8 es una opción adecuada cuando controlas la generación del archivo.
Ignorar el encabezado
with open("clientes.csv", encoding="utf-8", newline="") as archivo:
lector = csv.reader(archivo)
encabezado = next(lector, None)
for fila in lector:
print(fila)
next(lector, None) no genera una excepción si el archivo está vacío. Aun así, debes validar que el encabezado tenga las columnas esperadas.
Usar DictReader
DictReader convierte cada registro en un diccionario, lo que hace el código más claro:
with open("clientes.csv", encoding="utf-8", newline="") as archivo:
lector = csv.DictReader(archivo)
for cliente in lector:
print(cliente["nombre"], cliente["ciudad"])
Los nombres se obtienen de la primera fila. Puedes revisar lector.fieldnames y detectar columnas faltantes antes de procesar miles de registros.
requeridas = {"nombre", "email", "ciudad"}
actuales = set(lector.fieldnames or [])
faltantes = requeridas - actuales
if faltantes:
raise ValueError(f"Faltan columnas: {sorted(faltantes)}")
Trabajar con punto y coma
lector = csv.DictReader(
archivo,
delimiter=";",
)
También puedes detectar el dialecto con csv.Sniffer, pero no confíes completamente en una muestra pequeña:
muestra = archivo.read(4096)
archivo.seek(0)
dialecto = csv.Sniffer().sniff(muestra, delimiters=",;\t|")
lector = csv.DictReader(archivo, dialect=dialecto)
En procesos conocidos, es preferible configurar el separador explícitamente.
Convertir tipos
El módulo devuelve texto. Debes convertir números, fechas y booleanos:
from datetime import datetime
cantidad = int(fila["cantidad"])
precio = float(fila["precio"].replace(",", "."))
fecha = datetime.strptime(fila["fecha"], "%Y-%m-%d").date()
La conversión puede fallar. Registra el número de línea para localizar el problema:
for numero_linea, fila in enumerate(lector, start=2):
try:
cantidad = int(fila["cantidad"])
except (TypeError, ValueError):
print(f"Cantidad inválida en la línea {numero_linea}")
Escribir un CSV
import csv
filas = [
["Ana", "[email protected]", "Madrid"],
["Luis", "[email protected]", "Lima"],
]
with open("salida.csv", "w", encoding="utf-8", newline="") as archivo:
escritor = csv.writer(archivo)
escritor.writerow(["nombre", "email", "ciudad"])
escritor.writerows(filas)
writerow escribe una fila y writerows recibe un iterable de filas. El módulo añade comillas cuando son necesarias.
Escribir diccionarios con DictWriter
campos = ["nombre", "email", "ciudad"]
with open("salida.csv", "w", encoding="utf-8", newline="") as archivo:
escritor = csv.DictWriter(archivo, fieldnames=campos)
escritor.writeheader()
escritor.writerow({
"nombre": "Ana",
"email": "[email protected]",
"ciudad": "Madrid",
})
Si el diccionario contiene claves adicionales, configura extrasaction="ignore" solo cuando esa pérdida sea intencional. Normalmente es mejor detectar una estructura inesperada.
Añadir registros sin borrar el archivo
with open("registro.csv", "a", encoding="utf-8", newline="") as archivo:
escritor = csv.writer(archivo)
escritor.writerow(["2026-07-11", "Proceso completado"])
Antes de añadir, comprueba si el archivo existe y si ya contiene encabezado. La escritura simultánea desde varios procesos necesita coordinación para evitar datos mezclados.
Procesar archivos grandes
csv.reader trabaja de forma iterativa, por lo que no necesita cargar todo el archivo en memoria:
total = 0.0
with open("ventas.csv", encoding="utf-8", newline="") as archivo:
lector = csv.DictReader(archivo)
for fila in lector:
total += float(fila["total"])
print(total)
Este patrón es adecuado para millones de filas cuando solo necesitas una agregación. Si requieres filtros, uniones o agrupaciones complejas, Pandas puede resultar más cómodo, aunque consumirá más memoria si carga el archivo completo.
Filtrar y transformar registros
with open("ventas.csv", encoding="utf-8", newline="") as entrada, \
open("ventas_validas.csv", "w", encoding="utf-8", newline="") as salida:
lector = csv.DictReader(entrada)
escritor = csv.DictWriter(salida, fieldnames=lector.fieldnames)
escritor.writeheader()
for fila in lector:
try:
total = float(fila["total"])
except (TypeError, ValueError):
continue
if total >= 100:
fila["total"] = f"{total:.2f}"
escritor.writerow(fila)
En un sistema real, guarda los registros rechazados en otro archivo junto con el motivo. Descartarlos silenciosamente dificulta las auditorías.
Evitar fórmulas peligrosas
Cuando un CSV será abierto en Excel o herramientas similares, valores que empiezan con =, +, - o @ pueden interpretarse como fórmulas. Esto se conoce como CSV Injection. Si exportas datos proporcionados por usuarios, valida o neutraliza esos prefijos según las necesidades del sistema.
def seguro_para_hoja(valor):
texto = str(valor)
if texto.startswith(("=", "+", "-", "@")):
return "'" + texto
return texto
Manejar UTF-8 con BOM
Algunos archivos generados por Excel incluyen una marca BOM. Puedes leerlos con utf-8-sig:
with open("datos.csv", encoding="utf-8-sig", newline="") as archivo:
lector = csv.DictReader(archivo)
Esto evita que la primera columna aparezca con caracteres invisibles.
Crear una función reutilizable
from pathlib import Path
import csv
def leer_clientes(ruta):
ruta = Path(ruta)
if not ruta.is_file():
raise FileNotFoundError(ruta)
with ruta.open(encoding="utf-8-sig", newline="") as archivo:
lector = csv.DictReader(archivo)
requeridas = {"nombre", "email", "ciudad"}
faltantes = requeridas - set(lector.fieldnames or [])
if faltantes:
raise ValueError(f"Faltan columnas: {faltantes}")
for numero, fila in enumerate(lector, start=2):
yield numero, fila
El uso de yield mantiene el procesamiento incremental y facilita informar el número de línea.
Errores frecuentes
Los problemas más habituales son usar split, olvidar newline="", asumir que el separador siempre es coma, no definir la codificación, convertir números sin validar y sobrescribir el archivo original. También es frecuente confiar en que todas las filas tienen la misma cantidad de columnas.
Conclusión
El módulo csv ofrece todo lo necesario para leer, escribir y transformar archivos tabulares de forma eficiente. Utiliza DictReader para un código legible, valida encabezados, convierte tipos de manera explícita y registra filas inválidas. Con estas prácticas puedes automatizar intercambios de datos sin depender de procesos manuales ni introducir errores silenciosos.






