C# – Crear ficheros delimitados

No será la primera vez que en alguna de mis aplicaciones he tenido que crear ficheros de texto para intercambiar información con otras empresas. Para bancos, cajas de ahorro, ayuntamientos y un conjunto grande de entidades es moneda de cambio el trabajar con este tipo de documentos.

Aunque no es un punto excesivamente complicado, cada vez que he tenido que hacer un fichero ASCII delimitado he creado algún procedimiento que se encargase de este trabajo. Hoy me he propuesto crearme una clase que me permita generalizarme un poco la labor.

Si cogemos un ejemplo, como puede ser la creación de un fichero de adeudo por domiciliaciones (Norma 19), podríamos resumir diciendo que tiene la siguiente estructura:

  1. cabecera
  2. lista de datos
  3. resumen

Por tanto, con el fin de facilitar el trabajo, además de los procedimientos que necesitaremos para poder crear el fichero, deberemos tener unos métodos que nos permitan escribir una lista completa de datos o una línea de texto. Algo así como:

public class CamposDelimitados
 {
     /// <summary>
     /// Permite enviar todos los elementos de una lista al fichero de salida
     /// </summary>
     /// <param name="lista">Lista de elementos que deberán aparecer 
     /// en el fichero de salida</param>
     /// <remarks>La definición de la estructura de salida deberá especificarse 
     /// en el método ToString()</remarks>
     public void InsertaLista(IList lista)
     {
         throw new System.NotImplementedException();
     }


     /// <summary>
     /// Permite la inserción de una línea individual en el fichero de salida
     /// </summary>
     /// <param name="texto">Texto a incorporar en el fichero de salida</param>
     public void InsertaLinea(string texto)
     {
         throw new System.NotImplementedException();
     }
 }

Por tanto, una clase que tenga algún tipo de generalización y que sea útil para tal acción podría ser la siguiente:

using System.Collections;
using System.IO;
using System.Text;


namespace JnSoftware.Utiles
{

    /// <summary>
    /// Representa un escritor de ficheros delimitados.
    /// </summary>
    public class CamposDelimitados
    {

        private StringBuilder textoSalida = new StringBuilder();

        /// <summary>
        /// Permite enviar todos los elementos de una lista al 
        /// fichero de salida
        /// </summary>
        /// <param name="lista">Lista de elementos que deberán 
        /// aparecer en el fichero de salida</param>
        /// <remarks>La definición de la estructura de salida 
        /// deberá especificarse en el método ToString()</remarks>
        public void InsertaLista(IList lista)
        {
            foreach (object item in lista)
                InsertaLinea(item.ToString());
        }


        /// <summary>
        /// Permite la inserción de una línea.
        /// </summary>
        /// <param name="texto">Texto a incorporar en el fichero 
        /// de salida</param>
        public void InsertaLinea(string texto)
        {
            textoSalida.AppendLine(texto);
        }


        /// <summary>
        /// Crea el fichero de salida
        /// </summary>
        /// <param name="nombreFichero">Ruta completa del fichero 
        /// que contendrá los datos delimitados</param>
        public void creaFichero(string nombreFichero)
        {
            using (StreamWriter sw =
                new StreamWriter(nombreFichero, false, Encoding.UTF8))
            {
                sw.Write(textoSalida);
                sw.Flush();
                sw.Close();
            }
        }
    }
}

¿En qué me ayudará esta clase?. Es una pregunta sencilla de responder, puesto que fijándonos en el método InsertaLista, podremos deducir que bastará con crear una implementación propia del método ToString() de nuestras clases para definir la delimitación de los datos.

Claro que está que la implementación personalizada (o customizada, que últimamente está muy de moda este vocablo) del método ToString() representa un par de problemas adicionales, Por un lado, es algo personal ya que siempre me ha fastidiado bastante que hay empresas que suelen darte la columna inicial y la final y hay otras que te dan la longitud que deberá tener cada columna de datos; por otro lado, está la problemática de hacer que nuestras columnas de datos adquieran una longitud fija. Con la intención de solventar estos dos problemas, creo una serie de métodos estáticos en la misma clase para que agilicen la adaptación de los datos a la cadena resultante.

namespace JnSoftware.Utiles
{

    /// <summary>
    /// Describe el modo de alineación de un texto dentro 
    /// de un archivo delimitado
    /// </summary>
    public enum TextAlignment { Left, Right }


    /// <summary>
    /// Representa un escritor de ficheros delimitados.
    /// </summary>
    public class CamposDelimitados
    {

        /// <summary>
        /// Permite adaptar el contenido de un campo a un formato de tamaño fijo.
        /// </summary>
        /// <param name="campo">Contenido del campo en formato string</param>
        /// <param name="inicio">Entero que representa la posición 
        /// inicial en la delimitación</param>
        /// <param name="fin">Entero que representa la posición final en 
        /// la delimitación</param>
        /// <returns>Cadena preparada para insertar en el fichero</returns>
        public static string PreparaLinea(string campo, int inicio, int fin)
        {
            return PreparaLinea(campo, inicio, fin, TextAlignment.Left);
        }


        /// <summary>
        /// Permite adaptar el contenido de un campo a un formato de tamaño fijo.
        /// </summary>
        /// <param name="campo">Contenido del campo en formato string</param>
        /// <param name="longitudCampo">Longitud que deberá tener el campo 
        /// en la delimitación</param>
        /// <returns>Cadena preparada para insertar en el fichero</returns>
        public static string PreparaLinea(string campo, int longitudCampo)
        {
            return PreparaLinea(campo, longitudCampo, TextAlignment.Left);
        }


        /// <summary>
        /// Permite adaptar el contenido de un campo a un formato de tamaño fijo.
        /// </summary>
        /// <param name="campo">Contenido del campo en formato string</param>
        /// <param name="inicio">Entero que representa la posición inicial 
        /// en la delimitación</param>
        /// <param name="fin">Entero que representa la posición inicial en 
        /// la delimitación</param>
        /// <param name="alineacion">Alineación del texto en el fichero 
        /// delimitado</param>
        /// <returns>Cadena preparada para insertar en el fichero</returns>
        public static string PreparaLinea(
            string campo,
            int inicio,
            int fin,
            TextAlignment alineacion)
        {
            return PreparaLinea(campo, fin - inicio, alineacion);
        }


        /// <summary>
        /// Permite adaptar el contenido de un campo a un formato de tamaño fijo.
        /// </summary>
        /// <param name="campo">Contenido del campo en formato string</param>
        /// <param name="longitud">Longitud que deberá tener el campo en la 
        /// delimitación</param>
        /// <param name="alineacion">Alineación del texto en el fichero 
        /// delimitado</param>
        /// <returns>Cadena preparada para insertar en el fichero</returns>
        public static string PreparaLinea(
            string campo,
            int longitud,
            TextAlignment alineacion
            )
        {
            string espacios = new string(' ', longitud - campo.Length);
            if (alineacion == TextAlignment.Left)
                return campo + espacios;
            else
                return espacios + campo;
        }
    }
}

Por tanto, podemos decir que la clase, una vez finalizada, será la siguiente:

using System.Collections;
using System.IO;
using System.Text;


namespace JnSoftware.Utiles
{

    /// <summary>
    /// Describe el modo de alineación de un texto dentro 
    /// de un archivo delimitado
    /// </summary>
    public enum TextAlignment { Left, Right }


    /// <summary>
    /// Representa un escritor de ficheros delimitados.
    /// </summary>
    public class CamposDelimitados
    {

        private StringBuilder textoSalida = new StringBuilder();

        /// <summary>
        /// Permite enviar todos los elementos de una lista al 
        /// fichero de salida
        /// </summary>
        /// <param name="lista">Lista de elementos que deberán 
        /// aparecer en el fichero de salida</param>
        /// <remarks>La definición de la estructura de salida 
        /// deberá especificarse en el método ToString()</remarks>
        public void InsertaLista(IList lista)
        {
            foreach (object item in lista)
                InsertaLinea(item.ToString());
        }


        /// <summary>
        /// Permite la inserción de una línea.
        /// </summary>
        /// <param name="texto">Texto a incorporar en el fichero 
        /// de salida</param>
        public void InsertaLinea(string texto)
        {
            textoSalida.AppendLine(texto);
        }


        /// <summary>
        /// Crea el fichero de salida
        /// </summary>
        /// <param name="nombreFichero">Ruta completa del fichero 
        /// que contendrá los datos delimitados</param>
        public void creaFichero(string nombreFichero)
        {
            using (StreamWriter sw =
                new StreamWriter(nombreFichero, false, Encoding.UTF8))
            {
                sw.Write(textoSalida);
                sw.Flush();
                sw.Close();
            }
        }


        /// <summary>
        /// Permite adaptar el contenido de un campo a un formato de tamaño fijo.
        /// </summary>
        /// <param name="campo">Contenido del campo en formato string</param>
        /// <param name="inicio">Entero que representa la posición 
        /// inicial en la delimitación</param>
        /// <param name="fin">Entero que representa la posición final en 
        /// la delimitación</param>
        /// <returns>Cadena preparada para insertar en el fichero</returns>
        public static string PreparaLinea(string campo, int inicio, int fin)
        {
            return PreparaLinea(campo, inicio, fin, TextAlignment.Left);
        }


        /// <summary>
        /// Permite adaptar el contenido de un campo a un formato de tamaño fijo.
        /// </summary>
        /// <param name="campo">Contenido del campo en formato string</param>
        /// <param name="longitudCampo">Longitud que deberá tener el campo 
        /// en la delimitación</param>
        /// <returns>Cadena preparada para insertar en el fichero</returns>
        public static string PreparaLinea(string campo, int longitudCampo)
        {
            return PreparaLinea(campo, longitudCampo, TextAlignment.Left);
        }


        /// <summary>
        /// Permite adaptar el contenido de un campo a un formato de tamaño fijo.
        /// </summary>
        /// <param name="campo">Contenido del campo en formato string</param>
        /// <param name="inicio">Entero que representa la posición inicial 
        /// en la delimitación</param>
        /// <param name="fin">Entero que representa la posición inicial en 
        /// la delimitación</param>
        /// <param name="alineacion">Alineación del texto en el fichero 
        /// delimitado</param>
        /// <returns>Cadena preparada para insertar en el fichero</returns>
        public static string PreparaLinea(
            string campo,
            int inicio,
            int fin,
            TextAlignment alineacion)
        {
            return PreparaLinea(campo, fin - inicio, alineacion);
        }


        /// <summary>
        /// Permite adaptar el contenido de un campo a un formato de tamaño fijo.
        /// </summary>
        /// <param name="campo">Contenido del campo en formato string</param>
        /// <param name="longitud">Longitud que deberá tener el campo en la 
        /// delimitación</param>
        /// <param name="alineacion">Alineación del texto en el fichero 
        /// delimitado</param>
        /// <returns>Cadena preparada para insertar en el fichero</returns>
        public static string PreparaLinea(
            string campo,
            int longitud,
            TextAlignment alineacion
            )
        {
            string espacios = new string(' ', longitud - campo.Length);
            if (alineacion == TextAlignment.Left)
                return campo + espacios;
            else
                return espacios + campo;
        }
    }
}

¿Cómo utilizar la clase?

Creo un pequeño ejemplo en el que se muestra cómo podemos hacer uso de la clase en la creación de un fichero delimitado. En este ejemplo queda claro cómo definir el método ToString() para que se adapte al formato deseado:

using System;
using System.Collections.Generic;
using JnSoftware.Utiles;

namespace TextosDelimitadosTest
{
    class CreacionFicheroDelimitado
    {

        public int Id { get; set; }
        public string Texto { get; set; }
        public DateTime Fecha { get; set; }

        public override string ToString()
        {
            // En el método ToString() indicamos cómo deberá aparecer la información
            return 
                CamposDelimitados.PreparaLinea(Id.ToString(),1, 7, TextAlignment.Right)
                + CamposDelimitados.PreparaLinea(Texto, 30)
                + CamposDelimitados.PreparaLinea(Fecha.ToShortDateString(), 10);
                ;
        }

        // Ejecución de las pruebas
        public static void Main()
        {
            // Fichero de salida
            string fichero = @"C:Usersj.velascoDesktopexitTestsalida.txt";

            // Creación de una lista de ejemplo
            List<CreacionFicheroDelimitado> lista = 
                new List<CreacionFicheroDelimitado>();
            for (int i = 1; i < 20; i++)
            {
                lista.Add(
                    new CreacionFicheroDelimitado()
                    {
                        Id = i,
                        Texto = "Texto " + i.ToString(), 
                        Fecha = DateTime.Now 
                    }
                    );
            }

            // Creación del fichero
            CamposDelimitados cd = new CamposDelimitados();
            cd.InsertaLinea("-- Cabecera del texto");
            cd.InsertaLista(lista);
            cd.creaFichero(fichero);
        }
    }
}

6 thoughts on “C# – Crear ficheros delimitados

  1. no existe ningún api para manejar estas normas en C# ??

    Norma 19 Adeudo por domiciliaciones

    Norma 32 Descuento comercial en soporte magnético

    Norma 34 Transferencias nacionales, nominas, cheques, pagarés y pago certificado

    Norma 34.1 Transferencias nacionales e internacionales. Pagos en divisa

    Norma 43 Movimientos de Cuenta

    Norma 57 Cobros por Ventanilla

    Norma 58 Anticipo y gestión de cobro soporte magnético

    Norma 60 Recaudación de tributos y otros ingresos municipales.

    Norma 60 – anexo – Recaudación de tributos y otros ingresos municipales.

    Norma 68 Pagos domiciliados

    salu2grzfelizaño

  2. Pues la verdad es que no sé si existe algo ya hecho. Hasta el momento he visto programas ya finalizados que te permiten crear estos ficheros pero todos ellos incorporan sus propios sistemas de almacenamiento. Esto me ha dificultado siempre la creación de los ficheros desde mis datos.

    Hasta el momento sólo he tenido que crear ficheros que se adapten a la norma 19 de adeudos por domiciliaciones pero es un buen tema a estudiar. Lo tengo en cuenta, aunque igual da algún que otro problema intentar guardar alguna configuración de los recibos… Ya te digo, intento mirar cómo generalizarlo.

  3. Una pena la administración no haya puesto a disposición del público componentes (en Java al menos) con código para lageneración de ficheros de las normas. salu2felizaño

Responder a anonymous Cancelar respuesta

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