Ilustración conceptual de dos tablas que conservan las identidades y los campos de una entrega de puertas al intercambiar datos.

CSV: intercambiar tablas sin confundir separadores, tipos y codificación

El código 001 no es una forma tímida de escribir el número uno. Puede ser el identificador de una puerta. Si lo conviertes en número al importar una tabla, pierdes dos caracteres y quizá la correspondencia con el modelo. Después podrás presumir de que el archivo se abre sin errores, una distinción muy apreciada entre las automatizaciones que procesan el dato equivocado con absoluta tranquilidad.

CSV parece sencillo porque se puede leer como texto. Esa sencillez no decide qué separa las columnas, cómo se escriben las comillas ni si una celda representa un código o una medida. Vamos a definir esas decisiones, leer una tabla ficticia de puertas y escribir una copia en Python y C#. La comprobación será conservar los valores, incluidos los incómodos; todavía no vamos a corregir medidas ni actualizar un modelo.

Tabla de contenidos

Un CSV transporta campos; el significado necesita un acuerdo

En un archivo CSV, los registros contienen campos separados por un carácter acordado. La coma da nombre al formato, pero encontrarás también archivos delimitados por punto y coma. Esa diferencia importa si una medida se escribe como 0,90: un lector configurado para separar por comas puede interpretar dos campos donde esperabas uno. Cambiar la extensión del archivo no va a resolverlo; es una operación muy económica y, por desgracia, completamente ajena al contenido.

Para este ejercicio acordamos punto y coma como delimitador, comillas dobles para los campos que las necesiten, texto UTF-8 y tres columnas en este orden: codigo, descripcion, ancho_m. El nombre de la última deja visible que la medida se expresa en metros. Los códigos se conservan como texto. La medida también se lee inicialmente como texto: decidir cuándo convertirla y cómo evaluar su rango es una operación posterior.

Esa distinción evita dos errores comunes. La celda vacía de una medida no equivale al número cero, y las comillas que envuelven un código en CSV no garantizan que una hoja de cálculo vaya a tratarlo como texto. Al importar en esa aplicación debes configurar el tipo de la columna. Si ya se han eliminado los ceros y después guardas el resultado, el lector de Python no tiene manera de averiguar cuántos había antes. No es falta de inteligencia; es falta de información, que suele resultar menos vendible.

La descripción del formato en RFC 4180 explica el dialecto con comas y el uso de comillas. Nuestro ejemplo declara expresamente su variante con punto y coma. No vamos a deducirla por intuición ni a presentar cualquier archivo con extensión .csv como si cumpliera el mismo contrato.

Las comillas protegen el contenido que parece estructura

Crea una carpeta vacía para el ejercicio y guarda el siguiente contenido como puertas.csv, en UTF-8. La primera fila es la cabecera. La descripción de 001 contiene un punto y coma y unas comillas literales: las comillas exteriores delimitan el campo y cada comilla interior se representa duplicándola. La fila de 002 acaba con un punto y coma porque su último campo está vacío, no porque falte una columna.

				
					codigo;descripcion;ancho_m
001;"Puerta; acceso ""norte""";0,90
002;Sala técnica;
003;Salida de patio;1,20
				
			

Si separas cada línea con split(";"), la primera descripción se rompe en dos. Un lector CSV interpreta las comillas y sabe que ese separador pertenece al texto. También puede haber saltos de línea dentro de un campo entrecomillado; por eso recorrer líneas físicas y tratarlas siempre como registros completos es otro atajo con fecha de caducidad. Agradezco el entusiasmo por ahorrar una llamada a una biblioteca, pero preferiría que no saliera a pagar la descripción de una puerta.

El valor leído para la primera descripción debe ser Puerta; acceso "norte", con un punto y coma y unas comillas, sin las comillas exteriores de la representación CSV. El código seguirá siendo 001 y el ancho, el texto 0,90. La celda vacía de 002 será una cadena vacía. Que puedas representar esos tres casos no demuestra que la entrega esté validada; demuestra que has conseguido transportar lo que decía.

El acuerdo también incluye la forma de la tabla. Una fila con dos campos es distinta de una fila con tres cuyo último campo está vacío. Los lectores siguientes comprueban la cabecera y el número de campos antes de escribir la copia. Si una exportación cambia el delimitador o añade una columna, queremos enterarnos antes de producir un archivo nuevo con apariencia de normalidad.

Leer y escribir en Python sin convertir los códigos

Python incluye el módulo csv, así que este ejemplo no requiere instalar paquetes. Guarda el programa siguiente como copiar_csv.py, junto a la entrada. DictReader permite acceder a los campos por los nombres de cabecera. La codificación utf-8-sig acepta UTF-8 y elimina una marca BOM inicial cuando exista; no convierte un archivo de otra codificación por el mero hecho de pedírselo.

				
					import csv

campos = ["codigo", "descripcion", "ancho_m"]
with open("puertas.csv", encoding="utf-8-sig", newline="") as entrada:
    lector = csv.DictReader(entrada, delimiter=";", strict=True)
    if lector.fieldnames != campos:
        raise ValueError("Cabecera o delimitador distintos del contrato")
    filas = list(lector)
    for fila in filas:
        if None in fila or any(valor is None for valor in fila.values()):
            raise ValueError("Una fila no tiene exactamente tres campos")

with open("salida-python.csv", "x", encoding="utf-8", newline="") as salida:
    escritor = csv.DictWriter(salida, fieldnames=campos, delimiter=";")
    escritor.writeheader()
    escritor.writerows(filas)

print(f"Filas exportadas: {len(filas)}")
				
			

La apertura con newline="" deja que el módulo gestione los saltos de línea del formato. Al escribir, el lector no tiene que improvisar una unión de textos ni recordar dónde duplicar comillas: DictWriter construye la representación adecuada. La documentación de csv de Python 3.12 explica ambas clases y esa forma de abrir los archivos.

Ejecuta esta orden desde la carpeta del ejercicio. Obtendrás Filas exportadas: 3 y un archivo nuevo salida-python.csv. El modo "x" impide sobrescribir una copia que ya exista. Si quieres repetir la prueba, conserva o renombra la salida anterior antes de ejecutarla otra vez. La entrada no se modifica.

				
					python copiar_csv.py
				
			

La comprobación de None distingue campos ausentes o sobrantes de una celda vacía válida. En este contrato, "" se conserva. No añadimos una conversión automática a número ni una llamada a strip() sobre todos los valores: serían transformaciones del dato, no una consecuencia inevitable de leer CSV. Si decides aplicarlas en una herramienta real, define cuáles aceptas y qué información necesitas conservar para explicar el resultado.

En C# utiliza un lector de campos, no una cadena de Split

Para el ejemplo equivalente utilizaremos TextFieldParser, disponible para este proyecto .NET 8 a través de Microsoft.VisualBasic.FileIO. El nombre de ese espacio de nombres no obliga a escribir el programa en Visual Basic. Nos proporciona un lector de campos delimitados que podemos llamar desde C#, sin inventar un analizador de comillas en mitad del ejercicio.

Desde la misma carpeta crea el proyecto de consola con esta orden. Se genera una subcarpeta TablaCsv; sustituye su Program.cs por el contenido del siguiente bloque. Mantén puertas.csv en la carpeta del ejercicio y ejecuta después el proyecto desde allí, como indica la orden final. Si necesitas elegir otras ubicaciones, aplica la base explícita que vimos al trabajar con rutas.

				
					dotnet new console -n TablaCsv -f net8.0
				
			

El programa fija el delimitador, admite campos entrecomillados y desactiva el recorte de espacios. Primero lee y comprueba la estructura completa del ejemplo; después crea una salida nueva. Para escribir utilizamos una función corta que entrecomilla todos los campos y duplica sus comillas interiores. Es una elección explícita para este dialecto, no un reemplazo de un lector general de CSV.

				
					using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.VisualBasic.FileIO;

string[] campos = { "codigo", "descripcion", "ancho_m" };
var filas = new List<string[]>();
using (var lector = new TextFieldParser("puertas.csv", new UTF8Encoding(false, true), true))
{
    lector.SetDelimiters(";");
    lector.HasFieldsEnclosedInQuotes = true;
    lector.TrimWhiteSpace = false;
    string[] cabecera = lector.ReadFields() ?? throw new InvalidDataException("Falta la cabecera");
    if (!cabecera.SequenceEqual(campos))
        throw new InvalidDataException("Cabecera o delimitador distintos del contrato");

    while (!lector.EndOfData)
    {
        string[] fila = lector.ReadFields() ?? throw new InvalidDataException("Registro ausente");
        if (fila.Length != campos.Length)
            throw new InvalidDataException("Una fila no tiene exactamente tres campos");
        filas.Add(fila);
    }
}

using (var archivo = new FileStream("salida-csharp.csv", FileMode.CreateNew, FileAccess.Write))
using (var salida = new StreamWriter(archivo, new UTF8Encoding(false)))
{
    salida.WriteLine(string.Join(";", campos.Select(CampoCsv)));
    foreach (string[] fila in filas)
        salida.WriteLine(string.Join(";", fila.Select(CampoCsv)));
}

Console.WriteLine($"Filas exportadas: {filas.Count}");

static string CampoCsv(string valor)
{
    return "\"" + valor.Replace("\"", "\"\"") + "\"";
}
				
			

Ejecuta la orden siguiente. La copia de C# se llama salida-csharp.csv, de modo que puedes compararla con la de Python sin que una sustituya a la otra. CreateNew también evita sobrescribir una salida existente. La referencia de TextFieldParser permite comprobar la clase y sus opciones.

				
					dotnet run --project TablaCsv
				
			

Las dos copias no tienen por qué ser idénticas byte a byte. C# entrecomilla todos los campos de este ejemplo y Python solo los que lo necesitan. Eso puede representar exactamente la misma tabla. Compara los valores después de leer cada copia con el mismo contrato; un contador de comillas o el aspecto del texto en un editor no decide si el intercambio ha conservado los datos.

Comprueba los valores antes de dar el intercambio por terminado

La primera comprobación es concreta: en ambas salidas deben seguir existiendo los códigos 001, 002 y 003; la primera descripción debe conservar el punto y coma y las comillas; Sala técnica debe mantener su tilde; el ancho de 002 debe continuar vacío. Después puedes volver a usar una copia como entrada en otra carpeta de prueba y comprobar que recuperas esos mismos valores. Una exportación que vuelve a leerse correctamente es una evidencia más útil que una captura de tres columnas bien alineadas.

Prueba también un cambio deliberado del delimitador de la entrada, de punto y coma a coma. Nuestros programas deben rechazar la cabecera esperada, en lugar de escribir una tabla deformada. Una fila con un campo sobrante o con uno ausente también debe fallar. Son comprobaciones estructurales; todavía no deciden si un código está duplicado o si una medida es razonable para la puerta correspondiente.

Cuando necesites calcular, convierte únicamente los campos numéricos y con una convención explícita. En este ejemplo, ancho_m utiliza coma decimal y metros; no deberías interpretarlo según la configuración regional casual del ordenador. Guarda el texto original si necesitas explicar una corrección. El código, en cambio, sigue siendo un identificador: ni sumar sus dígitos ni convertirlo en entero mejora su utilidad. El formato del archivo no tiene autoridad para decidir eso por ti.

Una salida destinada a una hoja de cálculo exige además revisar cómo esa aplicación interpreta los campos, incluidas posibles fórmulas procedentes de datos externos. Entrecomillar es una regla de representación CSV, no una garantía de que el contenido sea seguro para cualquier uso posterior. Nuestro ejemplo conserva textos ficticios; una importación de datos de terceros necesita decidir ese tratamiento antes de distribuir la hoja.

Si una entrega te cambia 001 por 1 o separa una descripción en dos, ya sabes qué exigir antes de discutir sobre validaciones BIM: un contrato de columnas y una prueba de ida y vuelta que conserve los valores. El CSV no es especialmente complicado. Lo complicado empieza cuando cada aplicación interpreta una cosa distinta y todos anuncian que han exportado correctamente. En los comentarios puedes dejar el caso que más guerra te haya dado —sin datos del proyecto—: separadores, ceros iniciales o codificación. Al menos podremos discutir sobre el fallo concreto, que mejora bastante la conversación.

Artículos relacionados

Respuestas

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *