CSV Parece Fácil Hasta Que Llega una Coma Dentro de una Celda: RFC 4180 en la Práctica
split(",") funciona en la demo y muere en producción. Qué dice realmente el RFC 4180, las trampas que no dice (encoding, tipos, Excel) y cómo parsear y generar CSV sin romper nada.
La línea de código más mentirosa de la historia
const celdas = linea.split(',');
Todo programador ha escrito esto alguna vez. Y en la demo funciona: tu CSV de prueba tiene tres columnas de números limpios y todo va perfecto. Luego llega el primer dato real —una dirección con coma, un apellido con comillas, una observación con un salto de línea en medio— y el parser casero explota en silencio, produciendo columnas desplazadas que nadie detecta hasta que el informe de fin de mes no cuadra.
CSV es el formato que sobrevivió a todo: a XML, a JSON, a veinte bases de datos. Y la mayoría de los bugs que genera vienen de lo mismo: tratarlo como "texto separado por comas" en lugar de lo que es — un formato con reglas propias, documentadas en el RFC 4180 desde 2005.
Lo que el RFC 4180 sí dice (y casi nadie leyó)
El RFC es cortito y define siete reglas. Las que importan en la práctica:
Regla 1: los campos con caracteres especiales van entre comillas
Si un campo contiene coma, salto de línea o comillas, debe ir entrecomillado:
nombre,direccion,ciudad
Ana López,"Calle Mayor, 3, 2ºA",Madrid
Esa dirección tiene dos comas dentro del campo. split(',') te da cinco columnas donde hay tres.
Regla 2: las comillas dentro se escapan duplicándose
id,comentario
42,"El cliente dijo ""no tocar nada"" y se fue"
Una comilla doble dentro de un campo entrecomillado se escribe como "". Así que "dijo ""hola""" significa el texto dijo "hola". Si tu parser no entiende esto, cada cita textual de tus usuarios desplaza todas las columnas siguientes.
Regla 3: los saltos de línea pueden vivir dentro de un campo
Esta es la que rompe el segundo intento de todo el mundo — el parser que ya maneja comas pero lee línea a línea:
id,nota,estado
7,"Primera línea de la nota
segunda línea de la misma nota",activo
8,nota normal,pendiente
Este CSV tiene dos registros pero cuatro líneas de texto. Si iteras por líneas, el registro 7 aparece roto y el 8 bien. Por eso no puedes parsear CSV sin un mínimo de estado: tienes que saber si estás "dentro de comillas" o no, carácter a carácter.
Regla 4: los espacios son parte del campo
a, b, c
La segunda columna es " b" con espacio incluido. El RFC no contempla trim: si el generador puso un espacio, es dato. Muchos parsers lo recortan "por cortesía" y rompen datos legítimos.
Regla 5: todas las filas tienen el mismo número de campos
Teóricamente. En la práctica te llegarán filas cortas y largas, y tienes que decidir si eso es error o padding con vacíos. El RFC dice que debería ser consistente, no qué hacer cuando no lo es.
Lo que el RFC 4180 no dice (y te toca decidir)
Aquí vive el 80% de los problemas reales:
El encoding: la lotería invisible
El RFC no especifica encoding. Nada. Un CSV puede ser UTF-8, Latin-1, Windows-1252 o lo que a tu ERP de 2003 le pareció bien. Síntomas típicos: los ñ y acentos salen como ñ (UTF-8 leído como Latin-1) o como ? (carácter irreconocible).
La decisión práctica: genera siempre UTF-8, y si tu público abre con Excel, añade el BOM (EF BB BF al inicio). Sin BOM, Excel asume el encoding del sistema y tu UTF-8 perfecto se ve corrupto. Con BOM, Excel lo detecta bien. Sí, añadir tres bytes "invisibles" es feo — es el precio de que contabilidad pueda abrir tu exportación.
Los tipos: todo es texto (y Excel lo sabe)
CSV no tiene tipos. 007 es el string "007" hasta que algo lo interpreta. Y aquí entra el mayor destructor de datos del mundo corporativo: Excel interpreta agresivamente.
007→ 7 (adiós, código de empleado)1-2o3/4→ una fecha (adiós, rango de versiones)1E5→ 100000 (adiós, código de producto)12/10/2026→ fecha en el formato del sistema del usuario: 12 de octubre o 10 de diciembre según dónde esté instalado
Si generas CSVs que abrirán en Excel, las defensas son: prefijar con tab (\t007), usar el formato ="007" (hack compatible con Excel), o directamente exportar .xlsx. Ninguna es bonita. La realidad rara vez lo es.
Punto y coma: el CSV que no es CSV
En países donde la coma es el separador decimal (España, Alemania, Francia...), Excel decidió que el "CSV" local usa punto y coma. Resultado: dos archivos llamados .csv, uno con ; y otro con ,, y un mundo de parsers que fallan con uno de los dos.
Las librerías serias detectan el delimitador con un sniff (miran la primera línea y prueban). Si escribes el tuyo, prueba con ;, ,, \t y | — en ese orden de frecuencia — y elige el que produzca más columnas consistentes.
Saltos de línea: CRLF contra LF
El RFC manda CRLF. El mundo usa LF. La mayoría de parsers aceptan ambos. El caso límite: un campo entrecomillado que contiene \r\n en un archivo separado por \n. Parsea tolerante, genera estricto.
CSV injection: la trampa de seguridad que nadie ve venir
Esta merece sección propia porque es de las que dan incidentes. Si tu CSV se abre en una hoja de cálculo, los valores que empiezan por =, +, - o @ se interpretan como fórmulas:
nombre,comentario
Mallory,=2+5
Eve,=cmd|'/c calc'!A1
La segunda fila es un payload clásico: alguien mete ese texto en tu formulario de "comentarios", tú exportas a CSV, un analista lo abre en Excel, y Excel ejecuta la fórmula — en versiones antiguas con capacidad de lanzar comandos, en las nuevas al menos para exfiltrar datos vía HYPERLINK a un dominio externo.
La defensa estándar al generar CSVs que contendrán texto de usuarios: prefijar esos valores con una comilla simple '. La celda muestra el valor literal y no se evalúa. Y al parsear, ojo con reinyectar: si ese CSV alimenta otro sistema, recuerda que - inicial puede significar "negativo" en un lugar y "fórmula" en otro.
De CSV a JSON: las decisiones que importan
La conversión CSV → JSON parece trivial y tiene tres trampas:
- Todo llega como string:
"edad": "34". ¿Conviertes a número? ¿Y el007que parece número pero es un código? Regla sana: convierte solo si tu schema lo pide; si no, respeta el string. - Vacío contra null: una celda vacía
,,¿es""onull? El RFC no opina. Decide una convención y documéntala — mezclar ambas en la misma exportación es peor que cualquiera de las dos. - La cabecera es el contrato: si tu JSON usa la primera fila como claves, los duplicados y los nombres raros (
Fecha Altacon espacio y mayúscula) se convierten en decisiones de diseño. Normaliza las claves: minúsculas, snake_case, sin acentos.
Si quieres ver en vivo cómo se comportan estas decisiones, nuestro conversor CSV a JSON te deja probar con tus propios datos — incluidos los casos feos de comillas anidadas y campos multilínea. Y para el camino inverso, el conversor JSON a CSV aplana arrays de objetos aplicando las reglas del RFC: comillas donde hacen falta y escapes correctos.
Checklist para no morir en el intento
Al parsear CSV de terceros:
- Usa una librería con estado (sabe si está dentro de comillas), nunca
split - Detecta el delimitador (
;,,,\t,|) antes de asumir coma - Fuerza el encoding explícito; UTF-8 con o sin BOM por defecto, Latin-1 como sospechoso
- Tolera CRLF y LF; tolera filas con campos de más o de menos, pero repórtalos
- Decide
""vsnully no improvises fila a fila
Al generar CSV para humanos:
- UTF-8 con BOM si el destino es Excel
- Comillas en todo campo con coma, comilla o salto de línea;
""para escapar - Prefija
'a valores que empiecen por=,+,-,@si vienen de usuarios - Cabecera en snake_case, consistente y documentada
- Nunca confíes en que Excel respetará tus tipos: formatea pensando en él
CSV lleva vivo cincuenta años porque es el mínimo común denominador entre sistemas que no se hablan. Trátalo con el respeto de un protocolo real — no como texto con comas — y te devolverá el favor no rompiéndote un lunes por la mañana.
Related articles
URL Encoding: Por Qué %20 y + No Son lo Mismo (y Cuándo Importa)
El espacio se escribe %20 en una ruta pero + en un formulario, y confundirlos rompe búsquedas, firmas de APIs y enlaces de OAuth. Una guía práctica del percent-encoding para dejar de adivinar.
chmod 755 vs rwxr-xr-x: Domina los Permisos Unix de una Vez
Qué significa realmente rwxr-xr-x, cómo traducir entre octal y simbólico sin calculadora, los bits especiales que casi nadie entiende (setuid, setgid, sticky) y por qué chmod 777 "funciona" pero es una mala idea.