Comma-separated values, ou simplesmente arquivo delimitado por vírgulas, é um formato de arquivo que armazena dados tabelados. Não existe uma especificação formal para o formato, mas geralmente encontramos dois padrões, iremos programar uma classe que aceita esses dois padrões.

Resposta:

Padrão 1
aaa,bbb,ccc,ddd

Padrão 2

"aaa","bbb","ccc","ddd"

Em alguns casos já vi utilizando ';' ao invés de ',' , irei mostrar uma classe que trata os dois tipos de separadores. O nosso CSV Reader recebe um arquivo csv e retorna um datatable.

C#:

using System;
using System.Collections;
using System.Data;
using System.Text;
using System.IO;
using System.Collections.Generic;

namespace io.csv
{
    public class CsvParser
    {
        #region Atributos/Constantes/Propriedades
        private bool _quoted = false;
        private bool _headers = false;
        private string[] _sHeaders;
        private int _col = 1;

        private TextReader _stream;

        #endregion

        #region Construtor

        public CsvParser(TextReader stream, bool headers)
        {
            _stream = stream;
            _headers = headers;
        }

        public CsvParser(TextReader stream, string[] headers)
        {
            _stream = stream;
            _sHeaders = headers;
            _headers = true;
            _col = headers.Length;
        }

        #endregion

        #region Métodos

        private void analisarEstrutura(string linha)
        {
            bool dentroQuotes = false;

            //Verifica se primeiro caracter é "
            if (eQuote(linha[0]))
            {
                _quoted = true;
            }

            // Caso tenha passado os hearders via codigo, o numero de colunas já está determinado
            if (_sHeaders.Length > 0)
            {
                // Trata o numero de colunas com Quotes
                if (_quoted)
                {

                    foreach (char c in linha)
                    {
                        if (eQuote(c))
                        {
                            dentroQuotes = !dentroQuotes;
                        }
                        else
                        {
                            if (eSeparador(c) && !dentroQuotes)
                            {
                                _col++;
                            }
                        }
                    }
                }
                else
                {
                    // Trata o numero de colunas sem Quotes
                    foreach (char c in linha)
                    {
                        if (eSeparador(c))
                        {
                            _col++;
                        }
                    }

                }
            }
        } //analisarEstrutura

        private bool eQuote(char c)
        {
            return c == '"';
        } //eQuote

        private bool eSeparador(char c)
        {
            return c == ',' || c == ';';
        } //eSeparador

        public bool parser(out DataTable dataTable, Usuario usuario)
        {
           
            DataTable dt = new DataTable();
            string[] cabecalhos;
            string[] tokens;
                        bool sucesso;
           
                        sucesso = true;

            // Para aumentar a eficiencia poderia usar o ReadBlock ao inves do ReadLine
            // lendo assim uma quantidade de caracteres maiores a cada "ida" ao arquivo (buffer)
            // porém teria que tratar a quebra de linha
            string linha = _stream.ReadLine();


            // Analisando a Estrutura
            analisarEstrutura(linha);

            // Processa cabecalho
            if (_headers)
            {
                // headers passado via codigo
                if (_sHeaders.Length > 0)
                {
                    foreach (string s in _sHeaders)
                    {
                        dt.Columns.Add(s, typeof(string));
                    }
                }
                else
                {
                    //Processa cabecalhos com texto da primeira linha
                    cabecalhos = getTokens(linha);
                    foreach (string s in cabecalhos)
                    {
                        dt.Columns.Add(s, typeof(string));
                    }
                }
            }
            else
            {
                //Processa cabecalho sem texto
                for (int i = 0; i < _col; i++)
                {
                    dt.Columns.Add(String.Format("Coluna {0}", i + 1), typeof(string));
                }

                //Processa primeira linha                
                dt.Rows.Add(getTokens(linha));

            }

            while ((linha = _stream.ReadLine()) != null)
            {
                tokens = getTokens(linha);
                if (tokens.Length > _col)
                {
                                        // Erro! Linha fora do padrão
                                        sucesso = false;
                    break;

                }
                else
                {
                    dt.Rows.Add(tokens);
                }
            }

            if (sucesso)
            {
                dataTable = dt;
            }
            else
            {
                dataTable = null;
            }
           
            return sucesso;
        } //Parser

        private string[] getTokens(string linha)
        {
            bool dentroQuotes = false;
            List<String> tokens = new List<string>();
            StringBuilder sb = new StringBuilder();            
           
            if (_quoted)
            {
                // Tratando caso com quotes

                foreach (char c in linha)
                {
                    // é "
                    if (eQuote(c))
                    {
                        dentroQuotes = !dentroQuotes;
                    }
                    else
                    {
                        // Caso esteja dentro da area delimitada por " faz o append do caracter
                        if (dentroQuotes)
                        {
                            sb.Append(c);
                        }
                        else
                        {
                            // Fora da area delimitada pela " e encontrou um separador, salva a string na lista
                            if (eSeparador(c))
                            {
                                tokens.Add(sb.ToString());
                                sb = new StringBuilder();
                            }
                        }
                    }
                }
                // Acabou de ler a linha sem ler um separador
                // adiciona no vetor os caracteres que ainda estao no SB
                tokens.Add(sb.ToString());
            }
            else
            {
                // Tratando caso sem quotes

                foreach (char c in linha)
                {
                    // Caso seja separador, indica uma nova palavra foi encontrada
                    // Adiciona ela na lista e reinicia o StringBuilder
                    if (eSeparador(c))
                    {
                        tokens.Add(sb.ToString());
                        sb = new StringBuilder();
                    }
                }
                // Acabou de ler a linha sem ler um separador
                // adiciona no vetor os caracteres que ainda estao no SB
                tokens.Add(sb.ToString());
            }
            return tokens.ToArray();
        } //getTokens

        #endregion

    } //CsvReader

} //namespace



Tags: .net, c#, dotnet

Endereço de trackback para este post

Trackback URL (clique direito e copie atalho/localização do link)

Feedbacks esperando moderação

Esse post tem 2 feedbacks esperando moderação...

Deixe seu comentário


Seu endereço de e-mail não será revelado nesse site.

Sua URL será exibida.
(Quebras de linha se tornam <br />)
(Nome, e-mail & website)
(Permitir que usuários o contatem através de um formulário eletrônico (seu e-mail não será exibido.))