Analisando arquivos CSV em C #, com cabeçalho

266

Existe uma maneira padrão / oficial / recomendada de analisar arquivos CSV em C #? Eu não quero rolar meu próprio analisador.

Além disso, já vi casos de pessoas que usam ODBC / OLE DB para ler CSV por meio do driver de texto, e muitas pessoas desencorajam isso devido a seus "inconvenientes". Quais são essas desvantagens?

Idealmente, estou procurando uma maneira pela qual possa ler o CSV pelo nome da coluna, usando o primeiro registro como o cabeçalho / nome do campo. Algumas das respostas dadas estão corretas, mas funcionam basicamente para desserializar o arquivo em classes.

David Pfeffer
fonte

Respostas:

138

Deixe uma biblioteca lidar com todos os detalhes essenciais para você! :-)

Dê uma olhada no FileHelpers e mantenha-se SECO - não se repita - não há necessidade de reinventar a roda pela milionésima vez ....

Basicamente, você só precisa definir esse formato dos dados - os campos em sua linha individual no CSV - por meio de uma classe pública (e atributos bem pensados, como valores padrão, substituições por valores NULL e assim por diante), ponto o mecanismo FileHelpers em um arquivo e bingo - você recebe de volta todas as entradas desse arquivo. Uma operação simples - ótimo desempenho!

marc_s
fonte
1
até que você precisa sth realmente personalizado (ea maioria dos que podem ser implementadas como extensões de qualquer maneira) FileHelpers é de longe a melhor maneira de ir, realmente conveniente, testado e solução de bom desempenho
mikus
3
Desde 1º de junho de 2015, a única maneira de baixar o FileHelpers era procurá-lo no sourceforge.net. Aqui está o link usado: sourceforge.net/projects/filehelpers/?source=directory
Sudhanshu Mishra
2
@dotnetguy, estamos no caminho do release 3.1 (atualmente 3.1-rc2). Também nós redesenhamos o site: www.filehelpers.net você pode baixar a versão mais recente de lá
Marcos Meli
1
@MarcosMeli muito obrigado! Eu já usei o FileHelpers em um dos meus projetos e foi muito fácil de usar - parabéns para a equipe. Estou planejando um blog sobre ele em breve e entre - Adore o novo site - muito bem!
Sudhanshu Mishra 22/07/2015
O FileHelpers não manipula vírgulas entre CSVs corretamente ou mapeia os cabeçalhos dos campos, esperando que as colunas estejam na mesma ordem em que os campos são declarados no seu tipo. Eu não o usaria pessoalmente.
Alastair Maw
358

Um analisador de CSV agora faz parte do .NET Framework.

Adicione uma referência ao Microsoft.VisualBasic.dll (funciona bem em C #, não importa o nome)

using (TextFieldParser parser = new TextFieldParser(@"c:\temp\test.csv"))
{
    parser.TextFieldType = FieldType.Delimited;
    parser.SetDelimiters(",");
    while (!parser.EndOfData)
    {
        //Process row
        string[] fields = parser.ReadFields();
        foreach (string field in fields)
        {
            //TODO: Process field
        }
    }
}

Os documentos estão aqui - Classe TextFieldParser

PS Se você precisar de um exportador de CSV , tente CsvExport (discl: sou um dos contribuidores)

Alex
fonte
2
Pela minha experiência, o TextFieldParser não funciona bem com arquivos grandes (por exemplo,> 250Mb). :(
MBoros
6
TextFieldParser implementa IDisposable, portanto, pode ser melhor usá-lo em uma cláusula using. Boa resposta caso contrário.
Chris Bush
3
No construtor, você pode querer usar uma codificação diferente do que por padrão, assim: novo TextFieldParser ( "c: \ temp \ test.csv", System.Text.Encoding.UTF8)
neural5torm
1
Observe que, se algum campo no seu CSV contiver linhas em branco, elas serão ignoradas TextFieldParser.ReadLine(). Consulte TextFieldParser docs
mcNux 26/09/16
3
Existe uma maneira de obter isso no .NET Core?
Hugo Zink
183

O CsvHelper (uma biblioteca que mantenho) lerá um arquivo CSV em objetos personalizados.

var csv = new CsvReader( File.OpenText( "file.csv" ) );
var myCustomObjects = csv.GetRecords<MyCustomObject>();

Às vezes você não possui os objetos que está tentando ler. Nesse caso, você pode usar o mapeamento fluente porque não pode colocar atributos na classe.

public sealed class MyCustomObjectMap : CsvClassMap<MyCustomObject>
{
    public MyCustomObjectMap()
    {
        Map( m => m.Property1 ).Name( "Column Name" );
        Map( m => m.Property2 ).Index( 4 );
        Map( m => m.Property3 ).Ignore();
        Map( m => m.Property4 ).TypeConverter<MySpecialTypeConverter>();
    }
}

EDITAR:

O CsvReader agora exige que o CultureInfo seja passado para o construtor ( https://github.com/JoshClose/CsvHelper/issues/1441 ).

Exemplo:

var csv = new CsvReader(File.OpenText("file.csv"), System.Globalization.CultureInfo.CurrentCulture);
Josh Close
fonte
18
Eu concordo com @ kubal5003. O que me vendeu foi você tê-lo disponível como um pacote NuGet. Obrigado cara, é rápido e faz toda a leitura csv que eu preciso.
Gromer
7
É muito rápido. 1,3 milhão de registros lidos e desserializados em 10 segundos.
Marisks
2
Ótima biblioteca muito fácil de implementar. Eu gostaria apenas de sugerir que Josh atualizasse sua resposta aqui, porque a biblioteca mudou um pouco desde que essa resposta foi escrita e você não pode mais instanciar CsvHelper (agora é apenas um espaço para nome), mas é necessário usar a classe CsvReader.
Marko5
1
CsvClassMap parece não existir na última versão do CsvHelper?
knocte
1
knocte, é chamado ClassMap agora. Também existem outras alterações, como ter que fazer uma leitura antes de solicitar o registro do cabeçalho (que, a propósito, é definido como o que foi lido pela primeira chamada para Read ()). Como outros já mencionaram, é super rápido e fácil de trabalhar.
22817 norgie
31

Em um aplicativo comercial, eu uso o projeto Open Source em codeproject.com, CSVReader .

Funciona bem e tem bom desempenho. Há alguns testes comparativos no link que forneci.

Um exemplo simples, copiado da página do projeto:

using (CsvReader csv = new CsvReader(new StreamReader("data.csv"), true))
{
    int fieldCount = csv.FieldCount;
    string[] headers = csv.GetFieldHeaders();

    while (csv.ReadNextRecord())
    {
        for (int i = 0; i < fieldCount; i++)
            Console.Write(string.Format("{0} = {1};", headers[i], csv[i]));

        Console.WriteLine();
    }
}

Como você pode ver, é muito fácil trabalhar com isso.

alexn
fonte
12

Se você precisar apenas ler arquivos csv, recomendo esta biblioteca: Um leitor rápido de CSV
Se você também precisar gerar arquivos csv, use este: FileHelpers

Ambos são gratuitos e de código aberto.

Giorgi
fonte
O FileHelpers possui um resumo atraente: filehelpers.com O FileHelpers é uma biblioteca .NET gratuita e fácil de usar para importar / exportar dados de comprimento fixo ou registros delimitados em arquivos, strings ou fluxos.
AnneTheAgile 6/10/12
Embora esse link possa responder à pergunta, apenas as respostas do link são desencorajadas no Stack Overflow, você pode melhorar essa resposta pegando partes vitais do link e colocando-o na sua resposta, isso garante que sua resposta ainda seja uma resposta se o link for alterado ou removido :)
WhatsThePoint 24/10/18
11

Aqui está uma classe auxiliar que eu uso com frequência, caso alguém volte a esse segmento (eu queria compartilhá-lo).

Eu uso isso para a simplicidade de portá-lo em projetos prontos para uso:

public class CSVHelper : List<string[]>
{
  protected string csv = string.Empty;
  protected string separator = ",";

  public CSVHelper(string csv, string separator = "\",\"")
  {
    this.csv = csv;
    this.separator = separator;

    foreach (string line in Regex.Split(csv, System.Environment.NewLine).ToList().Where(s => !string.IsNullOrEmpty(s)))
    {
      string[] values = Regex.Split(line, separator);

      for (int i = 0; i < values.Length; i++)
      {
        //Trim values
        values[i] = values[i].Trim('\"');
      }

      this.Add(values);
    }
  }
}

E use-o como:

public List<Person> GetPeople(string csvContent)
{
  List<Person> people = new List<Person>();
  CSVHelper csv = new CSVHelper(csvContent);
  foreach(string[] line in csv)
  {
    Person person = new Person();
    person.Name = line[0];
    person.TelephoneNo = line[1];
    people.Add(person);
  }
  return people;
}

[Atualizado csv helper: bug corrigido onde o último caractere da nova linha criou uma nova linha]

Base33
fonte
17
se alguma das entradas csv contiver vírgula (,), esse código não funcionará.
hakan
Para manter as coisas leves, usei um caractere de cachimbo como separador. '|'
Base33
excelente solução. Apenas uma pergunta sobre o segundo snippet. Que tipo de objeto é Pessoa
Cocoa Dev
@CocoaDev É uma classe que contém duas propriedades de string - Name e TelephoneNo. Apenas para o exemplo. Se alguma das propriedades for um número inteiro, deve ser apenas uma conversão direta (com cheque?).
precisa saber é o seguinte
10

Esta solução está usando o assembly oficial Microsoft.VisualBasic para analisar o CSV.

Vantagens:

  • delimitador escapando
  • ignora o cabeçalho
  • aparar espaços
  • ignorar comentários

Código:

    using Microsoft.VisualBasic.FileIO;

    public static List<List<string>> ParseCSV (string csv)
    {
        List<List<string>> result = new List<List<string>>();


        // To use the TextFieldParser a reference to the Microsoft.VisualBasic assembly has to be added to the project. 
        using (TextFieldParser parser = new TextFieldParser(new StringReader(csv))) 
        {
            parser.CommentTokens = new string[] { "#" };
            parser.SetDelimiters(new string[] { ";" });
            parser.HasFieldsEnclosedInQuotes = true;

            // Skip over header line.
            //parser.ReadLine();

            while (!parser.EndOfData)
            {
                var values = new List<string>();

                var readFields = parser.ReadFields();
                if (readFields != null)
                    values.AddRange(readFields);
                result.Add(values);
            }
        }

        return result;
    }
Jonas_Hess
fonte
7

Eu escrevi TinyCsvParser for .NET, que é um dos analisadores .NET mais rápidos e altamente configuráveis ​​para analisar praticamente qualquer formato CSV.

É liberado sob a licença MIT:

Você pode usar o NuGet para instalá-lo. Execute o seguinte comando no Console do Gerenciador de Pacotes .

PM> Install-Package TinyCsvParser

Uso

Imagine que temos uma lista de pessoas em um arquivo CSV persons.csvcom seu nome, sobrenome e data de nascimento.

FirstName;LastName;BirthDate
Philipp;Wagner;1986/05/12
Max;Musterman;2014/01/02

O modelo de domínio correspondente em nosso sistema pode se parecer com isso.

private class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
}

Ao usar o TinyCsvParser, é necessário definir o mapeamento entre as colunas nos dados CSV e a propriedade no seu modelo de domínio.

private class CsvPersonMapping : CsvMapping<Person>
{

    public CsvPersonMapping()
        : base()
    {
        MapProperty(0, x => x.FirstName);
        MapProperty(1, x => x.LastName);
        MapProperty(2, x => x.BirthDate);
    }
}

E então podemos usar o mapeamento para analisar os dados CSV com a CsvParser.

namespace TinyCsvParser.Test
{
    [TestFixture]
    public class TinyCsvParserTest
    {
        [Test]
        public void TinyCsvTest()
        {
            CsvParserOptions csvParserOptions = new CsvParserOptions(true, new[] { ';' });
            CsvPersonMapping csvMapper = new CsvPersonMapping();
            CsvParser<Person> csvParser = new CsvParser<Person>(csvParserOptions, csvMapper);

            var result = csvParser
                .ReadFromFile(@"persons.csv", Encoding.ASCII)
                .ToList();

            Assert.AreEqual(2, result.Count);

            Assert.IsTrue(result.All(x => x.IsValid));

            Assert.AreEqual("Philipp", result[0].Result.FirstName);
            Assert.AreEqual("Wagner", result[0].Result.LastName);

            Assert.AreEqual(1986, result[0].Result.BirthDate.Year);
            Assert.AreEqual(5, result[0].Result.BirthDate.Month);
            Assert.AreEqual(12, result[0].Result.BirthDate.Day);

            Assert.AreEqual("Max", result[1].Result.FirstName);
            Assert.AreEqual("Mustermann", result[1].Result.LastName);

            Assert.AreEqual(2014, result[1].Result.BirthDate.Year);
            Assert.AreEqual(1, result[1].Result.BirthDate.Month);
            Assert.AreEqual(1, result[1].Result.BirthDate.Day);
        }
    }
}

Guia de usuario

Um Guia do Usuário completo está disponível em:

bytefish
fonte
1

Aqui está minha implementação do KISS ...

using System;
using System.Collections.Generic;
using System.Text;

class CsvParser
{
    public static List<string> Parse(string line)
    {
        const char escapeChar = '"';
        const char splitChar = ',';
        bool inEscape = false;
        bool priorEscape = false;

        List<string> result = new List<string>();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < line.Length; i++)
        {
            char c = line[i];
            switch (c)
            {
                case escapeChar:
                    if (!inEscape)
                        inEscape = true;
                    else
                    {
                        if (!priorEscape)
                        {
                            if (i + 1 < line.Length && line[i + 1] == escapeChar)
                                priorEscape = true;
                            else
                                inEscape = false;
                        }
                        else
                        {
                            sb.Append(c);
                            priorEscape = false;
                        }
                    }
                    break;
                case splitChar:
                    if (inEscape) //if in escape
                        sb.Append(c);
                    else
                    {
                        result.Add(sb.ToString());
                        sb.Length = 0;
                    }
                    break;
                default:
                    sb.Append(c);
                    break;
            }
        }

        if (sb.Length > 0)
            result.Add(sb.ToString());

        return result;
    }

}
Alex Begun
fonte
1
Isso não lida com quebras de linha entre cadeias de caracteres citadas, válidas em um arquivo CSV.
precisa saber é o seguinte
Alex, o que John está tentando dizer é que o RFC 4180 ( ietf.org/rfc/rfc4180.txt - consulte a seção 2 e o item 6) permite que uma coluna tenha uma CR LF no meio de uma coluna, espalhando-a efetivamente 2 linhas em um arquivo. Sua solução provavelmente funcionará bem na maioria dos casos (especialmente se os arquivos CSV foram criados salvando o Excel), mas não cobre esse caso extremo. CsvHelper, mencionado acima, deve levar esse caso em consideração.
David Yates
Sim, isso é verdade, mas se você tiver CR LF no seu CSV, é provável que não deva estar usando CSV, mas algo mais apropriado, como json, xml ou formato de comprimento fixo.
Alex Iniciada
1

Algum tempo atrás, eu escrevi uma classe simples para leitura / gravação de CSV com base na Microsoft.VisualBasicbiblioteca. Usando esta classe simples, você poderá trabalhar com CSV como na matriz de 2 dimensões. Você pode encontrar minha turma no seguinte link: https://github.com/ukushu/DataExporter

Exemplo simples de uso:

Csv csv = new Csv("\t");//delimiter symbol

csv.FileOpen("c:\\file1.csv");

var row1Cell6Value = csv.Rows[0][5];

csv.AddRow("asdf","asdffffff","5")

csv.FileSave("c:\\file2.csv");

Para ler apenas o cabeçalho, você precisa ler csv.Rows[0]células :)

Andrew
fonte
1

Solução de arquivo de origem única para necessidades simples de análise, útil. Lida com todos os casos extremos desagradáveis. Como normalização de nova linha e manipulação de novas linhas em literais de string entre aspas. Não há de quê!

Se o arquivo CSV tiver um cabeçalho, basta ler os nomes das colunas (e calcular os índices das colunas) da primeira linha. Simples assim.

Observe que esse Dumpé um método LINQPad, você pode removê-lo se não estiver usando o LINQPad.

void Main()
{
    var file1 = "a,b,c\r\nx,y,z";
    CSV.ParseText(file1).Dump();

    var file2 = "a,\"b\",c\r\nx,\"y,z\"";
    CSV.ParseText(file2).Dump();

    var file3 = "a,\"b\",c\r\nx,\"y\r\nz\"";
    CSV.ParseText(file3).Dump();

    var file4 = "\"\"\"\"";
    CSV.ParseText(file4).Dump();
}

static class CSV
{
    public struct Record
    {
        public readonly string[] Row;

        public string this[int index] => Row[index];

        public Record(string[] row)
        {
            Row = row;
        }
    }

    public static List<Record> ParseText(string text)
    {
        return Parse(new StringReader(text));
    }

    public static List<Record> ParseFile(string fn)
    {
        using (var reader = File.OpenText(fn))
        {
            return Parse(reader);
        }
    }

    public static List<Record> Parse(TextReader reader)
    {
        var data = new List<Record>();

        var col = new StringBuilder();
        var row = new List<string>();
        for (; ; )
        {
            var ln = reader.ReadLine();
            if (ln == null) break;
            if (Tokenize(ln, col, row))
            {
                data.Add(new Record(row.ToArray()));
                row.Clear();
            }
        }

        return data;
    }

    public static bool Tokenize(string s, StringBuilder col, List<string> row)
    {
        int i = 0;

        if (col.Length > 0)
        {
            col.AppendLine(); // continuation

            if (!TokenizeQuote(s, ref i, col, row))
            {
                return false;
            }
        }

        while (i < s.Length)
        {
            var ch = s[i];
            if (ch == ',')
            {
                row.Add(col.ToString().Trim());
                col.Length = 0;
                i++;
            }
            else if (ch == '"')
            {
                i++;
                if (!TokenizeQuote(s, ref i, col, row))
                {
                    return false;
                }
            }
            else
            {
                col.Append(ch);
                i++;
            }
        }

        if (col.Length > 0)
        {
            row.Add(col.ToString().Trim());
            col.Length = 0;
        }

        return true;
    }

    public static bool TokenizeQuote(string s, ref int i, StringBuilder col, List<string> row)
    {
        while (i < s.Length)
        {
            var ch = s[i];
            if (ch == '"')
            {
                // escape sequence
                if (i + 1 < s.Length && s[i + 1] == '"')
                {
                    col.Append('"');
                    i++;
                    i++;
                    continue;
                }
                i++;
                return true;
            }
            else
            {
                col.Append(ch);
                i++;
            }
        }
        return false;
    }
}
John Leidegren
fonte
1

Outro nesta lista, o Cinchoo ETL - uma biblioteca de código aberto para ler e gravar vários formatos de arquivo (CSV, arquivo simples, Xml, JSON etc.)

A amostra abaixo mostra como ler o arquivo CSV rapidamente (nenhum objeto POCO é necessário)

string csv = @"Id, Name
1, Carl
2, Tom
3, Mark";

using (var p = ChoCSVReader.LoadText(csv)
    .WithFirstLineHeader()
    )
{
    foreach (var rec in p)
    {
        Console.WriteLine($"Id: {rec.Id}");
        Console.WriteLine($"Name: {rec.Name}");
    }
}

A amostra abaixo mostra como ler o arquivo CSV usando o objeto POCO

public partial class EmployeeRec
{
    public int Id { get; set; }
    public string Name { get; set; }
}

static void CSVTest()
{
    string csv = @"Id, Name
1, Carl
2, Tom
3, Mark";

    using (var p = ChoCSVReader<EmployeeRec>.LoadText(csv)
        .WithFirstLineHeader()
        )
    {
        foreach (var rec in p)
        {
            Console.WriteLine($"Id: {rec.Id}");
            Console.WriteLine($"Name: {rec.Name}");
        }
    }
}

Confira os artigos no CodeProject sobre como usá-lo.

RajN
fonte
0

Baseado no post de unlimit em Como dividir corretamente um CSV usando a função C # split ()? :

string[] tokens = System.Text.RegularExpressions.Regex.Split(paramString, ",");

NOTA: isso não processa vírgulas com escape / aninhado, etc. e, portanto, é adequado apenas para determinadas listas simples de CSV.

radsdau
fonte
2
Isso é muito ruim e provavelmente lenta :)
EKS
1
Provavelmente, mas funciona de forma perfeita e simples para um pequeno conjunto de parâmetros, portanto, é uma solução válida e útil. Por que reduzir o voto? "Very Bad" é ​​um pouco extremo, você não acha?
Radsdau # 30/16
1
Ele não controla escapou / vírgulas aninhadas, etc. irá funcionar em alguns casos, mas definitivamente não vai funcionar para todos os arquivos CSV
NStuke
Você está certo; Vou editar a resposta para refletir isso. Obrigado. Mas ainda tem seu lugar.
radsdau
Isso funcionou perfeitamente no meu caso de uso em que estou criando uma dll sql server clr e não posso usar nenhum desses outros pacotes externos. Eu só precisava analisar um arquivo csv simples com um nome de arquivo e uma contagem de linhas.
dubvfan87
0

Este código lê csv para DataTable:

public static DataTable ReadCsv(string path)
{
    DataTable result = new DataTable("SomeData");
    using (TextFieldParser parser = new TextFieldParser(path))
    {
        parser.TextFieldType = FieldType.Delimited;
        parser.SetDelimiters(",");
        bool isFirstRow = true;
        //IList<string> headers = new List<string>();

        while (!parser.EndOfData)
        {
            string[] fields = parser.ReadFields();
            if (isFirstRow)
            {
                foreach (string field in fields)
                {
                    result.Columns.Add(new DataColumn(field, typeof(string)));
                }
                isFirstRow = false;
            }
            else
            {
                int i = 0;
                DataRow row = result.NewRow();
                foreach (string field in fields)
                {
                    row[i++] = field;
                }
                result.Rows.Add(row);
            }
        }
    }
    return result;
}
polina-c
fonte
1
O TextFieldParser está no Microsoft.VisualBasic.dll.
user3285954
0

Se alguém quiser um snippet, poderá inserir seu código sem precisar vincular uma biblioteca ou baixar um pacote. Aqui está uma versão que eu escrevi:

    public static string FormatCSV(List<string> parts)
    {
        string result = "";

        foreach (string s in parts)
        {
            if (result.Length > 0)
            {
                result += ",";

                if (s.Length == 0)
                    continue;
            }

            if (s.Length > 0)
            {
                result += "\"" + s.Replace("\"", "\"\"") + "\"";
            }
            else
            {
                // cannot output double quotes since its considered an escape for a quote
                result += ",";
            }
        }

        return result;
    }

    enum CSVMode
    {
        CLOSED = 0,
        OPENED_RAW = 1,
        OPENED_QUOTE = 2
    }

    public static List<string> ParseCSV(string input)
    {
        List<string> results;

        CSVMode mode;

        char[] letters;

        string content;


        mode = CSVMode.CLOSED;

        content = "";
        results = new List<string>();
        letters = input.ToCharArray();

        for (int i = 0; i < letters.Length; i++)
        {
            char letter = letters[i];
            char nextLetter = '\0';

            if (i < letters.Length - 1)
                nextLetter = letters[i + 1];

            // If its a quote character
            if (letter == '"')
            {
                // If that next letter is a quote
                if (nextLetter == '"' && mode == CSVMode.OPENED_QUOTE)
                {
                    // Then this quote is escaped and should be added to the content

                    content += letter;

                    // Skip the escape character
                    i++;
                    continue;
                }
                else
                {
                    // otherwise its not an escaped quote and is an opening or closing one
                    // Character is skipped

                    // If it was open, then close it
                    if (mode == CSVMode.OPENED_QUOTE)
                    {
                        results.Add(content);

                        // reset the content
                        content = "";

                        mode = CSVMode.CLOSED;

                        // If there is a next letter available
                        if (nextLetter != '\0')
                        {
                            // If it is a comma
                            if (nextLetter == ',')
                            {
                                i++;
                                continue;
                            }
                            else
                            {
                                throw new Exception("Expected comma. Found: " + nextLetter);
                            }
                        }
                    }
                    else if (mode == CSVMode.OPENED_RAW)
                    {
                        // If it was opened raw, then just add the quote 
                        content += letter;
                    }
                    else if (mode == CSVMode.CLOSED)
                    {
                        // Otherwise open it as a quote 

                        mode = CSVMode.OPENED_QUOTE;
                    }
                }
            }
            // If its a comma seperator
            else if (letter == ',')
            {
                // If in quote mode
                if (mode == CSVMode.OPENED_QUOTE)
                {
                    // Just read it
                    content += letter;
                }
                // If raw, then close the content
                else if (mode == CSVMode.OPENED_RAW)
                {
                    results.Add(content);

                    content = "";

                    mode = CSVMode.CLOSED;
                }
                // If it was closed, then open it raw
                else if (mode == CSVMode.CLOSED)
                {
                    mode = CSVMode.OPENED_RAW;

                    results.Add(content);

                    content = "";
                }
            }
            else
            {
                // If opened quote, just read it
                if (mode == CSVMode.OPENED_QUOTE)
                {
                    content += letter;
                }
                // If opened raw, then read it
                else if (mode == CSVMode.OPENED_RAW)
                {
                    content += letter;
                }
                // It closed, then open raw
                else if (mode == CSVMode.CLOSED)
                {
                    mode = CSVMode.OPENED_RAW;

                    content += letter;
                }
            }
        }

        // If it was still reading when the buffer finished
        if (mode != CSVMode.CLOSED)
        {
            results.Add(content);
        }

        return results;
    }
John
fonte
0

Aqui está uma solução curta e simples.

                using (TextFieldParser parser = new TextFieldParser(outputLocation))
                 {
                        parser.TextFieldType = FieldType.Delimited;
                        parser.SetDelimiters(",");
                        string[] headers = parser.ReadLine().Split(',');
                        foreach (string header in headers)
                        {
                            dataTable.Columns.Add(header);
                        }
                        while (!parser.EndOfData)
                        {
                            string[] fields = parser.ReadFields();
                            dataTable.Rows.Add(fields);
                        }
                    }
O Falcão
fonte