CSV es uno de esos formatos que nadie ama pero todos usan. Ha existido desde la década de 1970, todavía es el formato de exportación predeterminado de cada aplicación de hoja de cálculo del planeta, y probablemente está sentado en algún lugar de tu pipeline de datos ahora mismo. El formato parece trivialmente simple — solo valores separados por comas — pero analizarlo correctamente es sorprendentemente fácil de hacer mal. Vamos a revisar cómo funciona realmente, dónde se complica, y cómo manejarlo en Python y JavaScript sin dispararte en el pie.

Qué es realmente CSV

CSV significa Comma-Separated Values (valores separados por comas). Un archivo CSV es texto plano — sin codificación binaria, sin metadatos, sin estructura más allá de filas y columnas. Cada línea es un registro, cada valor dentro de una línea está separado por un delimitador (generalmente una coma), y la primera fila es convencionalmente una fila de encabezado con nombres de columnas. Así es como se ve un CSV real:

text
order_id,customer,product,quantity,unit_price,shipped
1001,Alice Nguyen,USB-C Hub,2,34.99,true
1002,Bob Martinez,Mechanical Keyboard,1,129.00,false
1003,Alice Nguyen,HDMI Cable,3,12.50,true
1004,Carol Smith,Webcam 1080p,1,79.95,false

Eso es todo — seis columnas, cuatro filas de datos y un encabezado. Sin corchetes angulares, sin llaves, sin comillas necesarias (todavía). Esta simplicidad es exactamente por qué CSV persiste: casi cualquier herramienta en la tierra puede abrirlo, leerlo y producirlo. Excel, Google Sheets, el comando COPY de PostgreSQL, pandas, read.csv() de R — todo CSV de fábrica. La desventaja es que el formato tiene casi ninguna estructura: sin tipos, sin anidamiento, sin esquema. Cada valor es una cadena hasta que tú decidas lo contrario.

RFC 4180 — El estándar que no se aplica realmente

Existe una especificación: RFC 4180, publicada en 2005. Define cosas como terminaciones de línea CRLF, escape con comillas dobles, y cómo manejar comas incrustadas. Pero aquí está el asunto — RFC 4180 es informativo, no un estándar. Está describiendo lo que ya era práctica común, no legislando. Nadie está obligado a seguirlo, y en la práctica, casi nadie lo sigue exactamente.

El resultado es el caos de dialectos CSV. Tienes archivos que usan LF en lugar de CRLF, archivos donde la primera fila puede o no ser un encabezado, archivos con una nueva línea final y archivos sin ella, archivos con un BOM UTF-8 añadido por Excel. El artículo de Wikipedia sobre CSV tiene un buen resumen de todo lo que varía en la práctica. El enfoque más seguro: nunca asumir nada sobre un archivo CSV que no produjiste tú mismo, y siempre usar un analizador apropiado en lugar de dividir ingenuamente por comas.

Reglas de comillas: cuando las comas aparecen dentro de los valores

Aquí es donde "solo dividir por comas" se rompe. ¿Qué pasa cuando un valor contiene una coma? ¿O una nueva línea? ¿O una comilla doble? RFC 4180 — y la mayoría de los analizadores reales — lo maneja envolviendo el campo en comillas dobles. Aquí está el conjunto completo de reglas:

  • Si un campo contiene una coma, una nueva línea o una comilla doble, envuelve todo el campo en comillas dobles
  • Si un campo contiene una comilla doble, escápala duplicándola ("")
  • Los campos entre comillas pueden abarcar varias líneas — la nueva línea se convierte en parte del valor
  • Los espacios dentro de las comillas son significativos y deben preservarse
text
order_id,customer,notes,unit_price
1005,David Lee,"Wants gift wrapping, express shipping",45.00
1006,Emma Brown,"Said: ""please handle with care""",89.99
1007,Frank Wu,"Address:
123 Main St
Apt 4B",15.50

En ese ejemplo: las notas de David contienen una coma, por lo que están entre comillas. La nota de Emma contiene comillas dobles, por lo que se duplican dentro de las comillas externas. La dirección de Frank abarca varias líneas — los saltos de línea son parte del valor. Un line.split(',') ingenuo en cualquier lenguaje va a estropear completamente los tres ejemplos. Por eso necesitas un analizador real.

La regla de oro del análisis CSV: Nunca dividir por comas manualmente. Usa siempre una biblioteca de análisis CSV dedicada. Maneja comillas, secuencias de escape, campos multilínea y codificación — nada de lo cual un simple split puede hacer correctamente.

Variantes de delimitadores: tabulaciones, puntos y comas, pipes

A pesar del nombre, CSV no tiene que usar comas. Varias variantes comunes usan diferentes delimitadores, y las encontrarás todas en la práctica:

  • TSV (Tab-Separated Values) — usa \t como delimitador. Común en bioinformática (salida BLAST, archivos VCF), exportaciones de bases de datos, y en cualquier lugar donde los propios valores frecuentemente contengan comas
  • Separado por punto y coma — el valor predeterminado en Excel para configuraciones regionales donde la coma es el separador decimal (Alemania, Francia, la mayor parte de la UE). Si alguna vez has abierto un CSV de un colega europeo y obtenido una sola columna gigante, esta es la razón
  • Separado por pipe — usa |. Común en exportaciones de datos bancarios y de seguros heredados donde las tabulaciones pueden ser eliminadas por sistemas mainframe
  • Ancho fijo — no es técnicamente CSV, pero a menudo se agrupa en la misma categoría. Las columnas se rellenan a anchos fijos en lugar de delimitarse

La mayoría de los analizadores CSV te permiten especificar el delimitador explícitamente. Cuando escribes un CSV que otros consumirán, documenta tu delimitador. Cuando lees uno que no produjiste, revisa las primeras líneas antes de asumir. El Formateador CSV puede ayudarte a inspeccionar y reformatear archivos con delimitadores no estándar.

Análisis de CSV en Python

La biblioteca estándar de Python incluye el módulo csv, y es genuinamente bueno. Las dos clases que usarás más son csv.reader para acceso fila-como-lista y csv.DictReader para acceso fila-como-diccionario (que es casi siempre lo que quieres).

python
import csv

# csv.reader — each row is a list of strings
with open('orders.csv', 'r', encoding='utf-8', newline='') as f:
    reader = csv.reader(f)
    header = next(reader)          # consume the header row
    for row in reader:
        order_id, customer, product, quantity, price, shipped = row
        print(f"Order {order_id}: {quantity}x {product} for {customer}")

# csv.DictReader — each row is an OrderedDict keyed by the header
with open('orders.csv', 'r', encoding='utf-8', newline='') as f:
    reader = csv.DictReader(f)
    for row in reader:
        if row['shipped'] == 'false':
            print(f"Pending: {row['order_id']} — {row['customer']}")

Dos cosas a incluir siempre: encoding='utf-8' (o lo que el archivo realmente use), y newline=''. Ese segundo es crítico — el módulo csv hace su propio manejo de nuevas líneas y necesita los bytes crudos. Sin newline='', puedes obtener filas en blanco adicionales en Windows.

Escribir es igual de sencillo con csv.writer:

python
import csv

orders = [
    {'order_id': 1008, 'customer': 'Grace Kim', 'product': 'Laptop Stand', 'quantity': 1, 'unit_price': 49.99, 'shipped': False},
    {'order_id': 1009, 'customer': 'Henry Park', 'product': 'USB-C Hub', 'quantity': 2, 'unit_price': 34.99, 'shipped': True},
]

with open('new_orders.csv', 'w', encoding='utf-8', newline='') as f:
    fieldnames = ['order_id', 'customer', 'product', 'quantity', 'unit_price', 'shipped']
    writer = csv.DictWriter(f, fieldnames=fieldnames)

    writer.writeheader()
    writer.writerows(orders)

# To use a different delimiter (e.g. tab-separated)
with open('new_orders.tsv', 'w', encoding='utf-8', newline='') as f:
    writer = csv.writer(f, delimiter='\t')
    writer.writerow(['order_id', 'customer', 'product'])
    writer.writerow([1008, 'Grace Kim', 'Laptop Stand'])

El módulo csv maneja todas las comillas automáticamente — si un campo contiene una coma o una nueva línea, lo envuelve en comillas dobles sin que tengas que pensarlo. Ese es el punto de usar una biblioteca.

Análisis de CSV en JavaScript / Node.js

JavaScript no tiene un analizador CSV incorporado. La tentación es hacer esto:

js
// ❌ Don't do this — breaks immediately on quoted fields
const rows = csvText.split('\n').map(line => line.split(','));

Eso falla en el momento en que cualquier valor contiene una coma, una nueva línea dentro de comillas, o una comilla doble escapada. Para cualquier cosa real, usa una biblioteca. PapaParse es el estándar de facto — es rápido, maneja todos los casos extremos, funciona tanto en el navegador como en Node.js, y soporta streaming para archivos grandes.

js
import Papa from 'papaparse';
import fs from 'fs';

// Parse a CSV string
const csvText = fs.readFileSync('orders.csv', 'utf8');

const result = Papa.parse(csvText, {
  header: true,          // first row becomes object keys
  skipEmptyLines: true,  // ignore blank lines at end of file
  dynamicTyping: true,   // converts "true"/"false" to booleans, numbers to numbers
});

console.log(result.data);
// [
//   { order_id: 1001, customer: 'Alice Nguyen', product: 'USB-C Hub', quantity: 2, unit_price: 34.99, shipped: true },
//   { order_id: 1002, customer: 'Bob Martinez', product: 'Mechanical Keyboard', quantity: 1, unit_price: 129, shipped: false },
//   ...
// ]

// Check for parse errors
if (result.errors.length > 0) {
  console.error('Parse errors:', result.errors);
}

// Generate CSV from an array of objects
const orders = [
  { order_id: 1008, customer: 'Grace Kim', product: 'Laptop Stand', quantity: 1, unit_price: 49.99 },
  { order_id: 1009, customer: 'Henry Park', product: 'USB-C Hub', quantity: 2, unit_price: 34.99 },
];

const csvOutput = Papa.unparse(orders);
fs.writeFileSync('export.csv', csvOutput, 'utf8');

La opción dynamicTyping es útil pero vale la pena conocer — convierte cosas como "34.99" a un número automáticamente. Eso es generalmente lo que quieres, pero puede sorprenderte si un campo como order_id resulta ser un número en el CSV pero lo querías como cadena. Desactívalo si necesitas salida de cadena estricta.

Para conversiones rápidas entre CSV y otros formatos, la herramienta CSV a JSON maneja la mayoría de los casos comunes en el navegador sin ningún código — útil para transformaciones de datos puntuales. También hay CSV a XML si necesitas alimentar los datos a un sistema basado en XML.

Trampas comunes que definitivamente encontrarás

Incluso con un buen analizador, CSV tiene algunas minas de tierra bien conocidas. Aquí están las que aparecen con más frecuencia:

  • Bytes BOM de Excel. Cuando Excel exporta un CSV como "UTF-8", a menudo añade un BOM UTF-8 (EF BB BF en hexadecimal, o \ufeff como carácter). Esto hace que el primer encabezado de columna parezca order_id en lugar de order_id. En Python, abre el archivo con encoding='utf-8-sig' en lugar de utf-8 para eliminarlo automáticamente. PapaParse lo maneja de forma transparente.
  • Terminaciones de línea CRLF vs LF. RFC 4180 especifica CRLF (\r\n), pero las herramientas Unix producen LF (\n) y los archivos Mac antiguos usan CR (\r) solo. Eso es por qué el módulo csv de Python necesita newline='' — maneja los tres internamente. Si estás leyendo bytes crudos y dividiendo manualmente, necesitas eliminar \r explícitamente.
  • Problemas de codificación. CSV no tiene forma de declarar su propia codificación — a diferencia del <meta charset> de HTML o el <?xml encoding="..."?> de XML. Excel a menudo guarda archivos en Windows-1252 (también conocido como CP1252) en lugar de UTF-8, lo que corrompe los caracteres acentuados. Si ves caracteres como é en lugar de é, tienes un archivo UTF-8 decodificado como Latin-1, o viceversa. Siempre establece la codificación fuera de banda con quien produzca el archivo.
  • Números que parecen fechas. Excel convierte silenciosamente valores como 1-2 o 03/04 a fechas al abrir o guardar. Si estás exportando códigos de productos o números de versión, añade un apóstrofe en Excel ('1-2) para evitar esto — o dile a quien produce el archivo que lo haga.
  • Comas finales. Algunos exportadores emiten una coma final al final de cada línea, lo que crea una columna vacía fantasma. Un analizador robusto la ignora; un split ingenuo crea un elemento de cadena vacío adicional.

Si estás tratando con un archivo que parece raro, el Validador CSV puede decirte rápidamente si el archivo está bien formado y señalar problemas de codificación o estructura antes de que intentes procesarlo.

CSV vs JSON vs Excel — Cuándo usar qué

Estos tres formatos se superponen mucho en la práctica, pero cada uno tiene un punto ideal claro:

  • Usa CSV cuando estés moviendo datos tabulares planos entre sistemas — exportaciones de bases de datos, pipelines de análisis, importaciones de hojas de cálculo, carga masiva de datos. Es universalmente compatible, diminuto en tamaño, y trivialmente diferenciable en git. La restricción: es plano. Sin anidamiento, sin tipos, sin relaciones.
  • Usa JSON cuando los datos son jerárquicos o el esquema importa. Un pedido con múltiples líneas de artículos, un archivo de configuración con objetos anidados, una respuesta de API — estos son naturalmente JSON. CSV te obligaría a desnormalizar los datos o inventar tu propia convención de anidamiento. La especificación JSON es clara e inequívoca; el formato preserva los tipos (números, booleanos, null, arrays, objetos).
  • Usa Excel (.xlsx) cuando la salida es para humanos, no para máquinas. Formato, fórmulas, múltiples hojas, gráficos — si un usuario de negocio es el consumidor final, Excel a menudo es la elección correcta. Solo nunca lo uses como formato de intercambio entre sistemas. La especificación OOXML es enorme y el formato es frágil entre versiones.

Hay una heurística práctica: si naturalmente verías los datos en una hoja de cálculo, CSV probablemente está bien. Si los verías en un árbol o un editor de código, usa JSON. Si los envías a un interesado de negocios que filtrará y ordenará, usa Excel.

Conclusión

CSV es simple por diseño y engañosamente complicado en la práctica. El formato no tiene un estándar aplicado, viene en múltiples variantes de delimitadores, y depende de reglas de comillas que rompen todo intento de análisis manual. La solución siempre es la misma: usa un analizador real (el módulo csv integrado de Python, o PapaParse en JavaScript), siempre especifica la codificación explícitamente, y ten cuidado con los bytes BOM de Excel. Una vez que tengas un analizador confiable en su lugar, CSV es en realidad un gran formato — rápido de producir, fácil de inspeccionar en cualquier editor de texto, y compatible en todas partes. Para el trabajo diario con archivos CSV, las herramientas CSV a JSON, Formateador CSV, y Validador CSV manejan las operaciones comunes sin escribir una línea de código.