C# -  Salvando uma lista de objetos em um arquivo texto


Hoje vamos recordar uma operação básica que todo o desenvolvedor C# deve saber:  salvar uma lista para um arquivo texto.

Eu já apresentei os fundamentos sobre como ler e escrever em arquivos textos neste artigo :  C# -  Lendo e escrevendo em arquivos textos (StreamReader/StreamWriter)

Hoje vamos aplicar esses conceitos em uma tarefa prática bem comum : Salvar uma lista para um texto.

Então vamos supor que você tenha uma lista de objetos representado por List<T> e deseja salvar esta lista em um arquivo no formato .txt.

Vamos criar uma aplicação console (.NET Core) List_Text1 no ambiente do .NET 5.0 .

No projeto crie uma pasta Data e nesta pasta defina a classe Contato que representa o nosso objeto para o qual vamos gerar a lista e a seguir gerar o arquivo texto.

public class Contato
{
        public int Id { get; set; }
        public string Nome { get; set; }
        public string Email { get; set; }
        public string Telefone { get; set; }
}

A seguir vamos criar a pasta Services e nesta pasta criar a classe ContatoService onde vamos criar os seguintes métodos:

  1. GetContatos() - Retorna uma lista de objetos do tipo Contato;
  2. SalvarListaParaTexto() - Salva a lista de objetos para uma arquivo texto usando um separador;
  3. ExibirArquivoTexto() - Exibe o conteúdo do arquivo texto gerado;

Todos os métodos descritos acima serão estáticos de forma a podermos usá-los sem necessitar da instância da classe.

1- GetContatos()

public static List<Contato> GetContatos()
{
   var lista = new List<Contato> {
      new Contato() {Id = 1, Nome = "Maria", Email = "maria@email.com", Telefone="13-8760-9909"},
      new Contato() {Id = 2, Nome = "Paulo", Email = "paulo@email.com", Telefone="12-9981-0919"},
      new Contato() {Id = 3, Nome = "Beatriz", Email = "bibi@yahoo.com", Telefone="11-9956-4098"},
      new Contato() {Id = 4, Nome = "Janice", Email = "jan@hotmail.com", Telefone="21-9850-0034"},
      new Contato() {Id = 5, Nome = "Mario", Email = "mario@email.com", Telefone="17-6580-1122"}
    };
   return lista;
}

Neste código estamos gerando uma lista com 5 objetos Contato e retornando a lista.

2- SalvarListaParaTexto(IEnumerable<T> lista,  string arquivo, char separador)

 public static bool SalvarListaParaTexto<T>(IEnumerable<T> lista, string arquivo,
           char separador)
 {
            try
            {
                using (var sw = File.CreateText(arquivo))
                {
                    var plist = typeof(T).GetProperties().Where(p => p.CanRead &&
                                (p.PropertyType.IsValueType || p.PropertyType == typeof(string))
                                && p.GetIndexParameters().Length == 0).ToList();

                    if (plist.Count > 0)
                    {
                        var seperator = separador.ToString();

                        sw.WriteLine(string.Join(seperator, plist.Select(p => p.Name)));

                        foreach (var item in lista)
                        {
                            var values = new List<object>();
                            foreach (var p in plist) values.Add(p.GetValue(item, null));
                            sw.WriteLine(string.Join(seperator, values));
                        }
                    }
                }
                return true;
            }
            catch
            {
                throw;
            }
  }

Neste código estamos recebendo a lista de objetos como um IEnumerable<T> assim podemos ter como fonte tabelas, listas, dicionários, etc. Recebemos também o nome do arquivo texto que será gerado e o separador das colunas que serão geradas.

Criamos o arquivo texto usando o método File.CreateText() em uma declaração using para liberar os recursos usando a interface IDisposable. Em seguida, precisamos coletar as informações das propriedades do tipo T. Pedimos as informações das propriedades que podemos ler, o tipo de propriedade é um tipo de valor ou string e não é um indexador.

Se a lista de informações da propriedade contiver pelo menos uma informação de propriedade, podemos continuar. Precisamos gerar os cabeçalhos das colunas. Usamos para isso o método string.Join para construir o cabeçalho da coluna da lista de informações da propriedade.

Agora podemos começar a escrever as linhas de dados para cada objeto em nossa lista. E faremos isso em 4 etapas:

1- Precisamos criar uma lista genérica de objetos para nossos dados;
2- Para cada informação de propriedade na lista de informações de propriedade, coletamos um valor e o armazenamos na lista de dados;
3- A seguir construímos a linha usando a função string.Join e a salvamos em um arquivo;
4- No final, fechamos um arquivo (o método Dispose fará isso);

3- ExibirArquivoTexto(string path)

public static void ExibirArquivoTexto(string path)
{
            if (File.Exists(path))
            {
                using (StreamReader sr = File.OpenText(path))
                {
                    string s = "";
                    while ((s = sr.ReadLine()) != null)
                    {
                        Console.WriteLine(s);
                    }
                }
            }
            else
            {
                Console.WriteLine($"Arquivo {path} não encontrado");
            }
 }

Neste código passamos o caminho e nome do arquivo texto gerado, e, após verificar a sua existência percorremos o arquivo e exibimos o seu conteúdo.

Testando a implementação

Na classe Program vamos definir o código abaixo e testar a nossa implementação

using List_Text1.Services;
using System;

namespace List_Text1
{
    class Program
    {
        static void Main(string[] args)
        {
            var caminhoArquivo = @"c:\dados\txt\ListaContatos.txt";

            Console.WriteLine("Salvando uma lista de objetos em um arquivo texto\n");
            var lista = ContatoService.GetContatos();

            try
            {
                ContatoService.SalvarListaParaTexto(lista, caminhoArquivo, '\t');
                ContatoService.ExibirArquivoTexto(caminhoArquivo);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Erro ao gerar arquivo texto " + ex.Message);
            }
        }
    }
}

No código acima estamos usando como separado a tabulação '\t'.

A execução do projeto irá apresentar o seguinte resultado:

Podemos ainda implementar a opção para não salvar o header no arquivo texto incluindo o parâmetro cabecalho :

 public static void ExportarParaArquivo<T>(this List<T> listaDados, string nomeArquivo, char ColunaSeparador, bool cabecalho)

e alterando a linha de código :

 if(cabecalho)
      sw.WriteLine(string.Join(seperador, lista.Select(p => p.Name)));

A chamada agora pode ser feita assim: <lista>.ExportarParaArquivo(arquivo, ',', false);

Pegue o código do projeto aqui :   List_Text1.zip

"Toda a Escritura é divinamente inspirada, e proveitosa para ensinar, para redargüir, para corrigir, para instruir em justiça;"
2 Timóteo 3:16

Referências:


José Carlos Macoratti