Análisis de archivos CSV en C #, con encabezado

266

¿Existe una forma predeterminada / oficial / recomendada de analizar archivos CSV en C #? No quiero rodar mi propio analizador.

Además, he visto casos de personas que utilizan ODBC / OLE DB para leer CSV a través del controlador de texto, y muchas personas desalientan esto debido a sus "inconvenientes". ¿Cuáles son estos inconvenientes?

Idealmente, estoy buscando una manera a través de la cual pueda leer el CSV por nombre de columna, usando el primer registro como los nombres de encabezado / campo. Algunas de las respuestas dadas son correctas, pero básicamente trabajan para deserializar el archivo en clases.

David Pfeffer
fuente

Respuestas:

138

¡Deje que una biblioteca maneje todos los detalles esenciales por usted! :-)

Echa un vistazo a FileHelpers y mantente SECO: no te repitas, no es necesario reinventar la rueda una millonésima vez ...

Básicamente, solo necesita definir esa forma de sus datos, los campos en su línea individual en el CSV, mediante una clase pública (y atributos tan bien pensados ​​como valores predeterminados, reemplazos para valores NULL, etc.), señale el motor FileHelpers en un archivo y bingo: recupera todas las entradas de ese archivo. Una operación simple: ¡excelente rendimiento!

marc_s
fuente
1
hasta que necesite algo realmente personalizado (y la mayor parte de eso se puede implementar como extensiones de todos modos) FileHelpers es, con mucho, la mejor manera de ir, una solución realmente conveniente, probada y con buen rendimiento
mikus
3
A partir del 1 de junio de 2015, la única forma en que podía descargar FileHelpers era buscarlo en sourceforge.net. Aquí está el enlace utilizado: sourceforge.net/projects/filehelpers/?source=directory
Sudhanshu Mishra
2
@dotnetguy estamos en el camino para lanzar 3.1 (actualmente 3.1-rc2) está fuera. También rediseñamos el sitio: www.filehelpers.net puede descargar la última versión desde allí
Marcos Meli
1
@MarcosMeli muchas gracias! Ya usé FileHelpers en uno de mis proyectos y fue muy fácil de usar: felicitaciones al equipo. Estoy planeando un blog pronto y, por cierto, me encanta el nuevo sitio, ¡bien hecho!
Sudhanshu Mishra
FileHelpers no maneja las comillas entre comillas en los CSV correctamente, o en realidad asigna los encabezados de campo, esperando en cambio que las columnas estén en el mismo orden en que los campos se declaran en su tipo. No lo usaría, personalmente.
Alastair Maw
358

Un analizador CSV ahora es parte de .NET Framework.

Agregue una referencia a Microsoft.VisualBasic.dll (funciona bien en C #, no importa el nombre)

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
        }
    }
}

Los documentos están aquí - Clase TextFieldParser

PD: si necesita un exportador CSV , pruebe CsvExport ( discl : soy uno de los contribuyentes)

Alex
fuente
2
Desde mi experiencia, TextFieldParser no funciona bien con archivos grandes (por ejemplo,> 250Mb). :(
MBoros
66
TextFieldParser implementa IDisposable, por lo que podría ser mejor usarlo en una cláusula de uso. Buena respuesta de lo contrario.
Chris Bush
3
En el constructor, es posible que desee utilizar una codificación diferente a la predeterminada, como sigue: new TextFieldParser ("c: \ temp \ test.csv", System.Text.Encoding.UTF8)
neural5torm
1
Tenga en cuenta que si algún campo en su CSV contiene líneas en blanco, se omitirán TextFieldParser.ReadLine(). Ver documentos de TextFieldParser
mcNux
3
¿Hay alguna manera de obtener esto en .NET Core?
Hugo Zink
183

CsvHelper (una biblioteca que mantengo) leerá un archivo CSV en objetos personalizados.

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

A veces no eres dueño de los objetos en los que estás intentando leer. En este caso, puede usar el mapeo fluido porque no puede poner atributos en la clase.

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:

CsvReader ahora requiere que CultureInfo se pase al constructor ( https://github.com/JoshClose/CsvHelper/issues/1441 ).

Ejemplo:

var csv = new CsvReader(File.OpenText("file.csv"), System.Globalization.CultureInfo.CurrentCulture);
Josh Close
fuente
18
Estoy de acuerdo con @ kubal5003. Lo que me vendió fue que lo tienes disponible como un paquete NuGet. Gracias hombre, es rápido y hace toda la lectura de csv que necesito.
Gromer
77
Es muy rápido. 1.3 millones de registros leídos y deserializados en 10 segundos.
Marisks
2
Gran biblioteca muy fácil de implementar. Solo sugeriría a Josh que actualice su respuesta aquí porque la biblioteca ha cambiado un poco desde que se escribió esta respuesta y ya no puede crear una instancia de CsvHelper (ahora es solo un espacio de nombres), pero debe usar la clase CsvReader.
Marko
1
¿CsvClassMap no parece existir en la última versión de CsvHelper?
knocte
1
knocte, ahora se llama ClassMap. También hay otros cambios, como tener que hacer una lectura antes de pedir el registro de encabezado (que por cierto se establece en lo que fue leído por la primera llamada a Read ()). Como otros han mencionado antes, es súper rápido y fácil de trabajar.
norgie
31

En una aplicación de negocios, uso el proyecto Open Source en codeproject.com, CSVReader .

Funciona bien y tiene buen rendimiento. Hay algunos puntos de referencia en el enlace que proporcioné.

Un ejemplo simple, copiado de la página del proyecto:

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 puede ver, es muy fácil trabajar con él.

alexn
fuente
12

Si solo necesita leer archivos csv, le recomiendo esta biblioteca: un lector CSV rápido
Si también necesita generar archivos csv, use este: FileHelpers

Ambos son gratuitos y de código abierto.

Giorgi
fuente
FileHelpers tiene un resumen atractivo: filehelpers.com FileHelpers es una biblioteca .NET gratuita y fácil de usar para importar / exportar datos de registros de longitud fija o delimitados en archivos, cadenas o secuencias.
AnneTheAgile
Si bien este enlace puede responder a la pregunta, se desaconsejan las respuestas de solo enlace en Stack Overflow, puede mejorar esta respuesta tomando partes vitales del enlace y poniéndola en su respuesta, esto asegura que su respuesta siga siendo una respuesta si se cambia el enlace o eliminado :)
WhatsThePoint
11

Aquí hay una clase auxiliar que uso a menudo, en caso de que alguien vuelva a este hilo (quería compartirlo).

Lo uso por la simplicidad de portarlo a proyectos listos para usar:

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);
    }
  }
}

Y úsalo 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;
}

[Ayuda CSV actualizada: error corregido donde el último personaje de la nueva línea creó una nueva línea]

Base33
fuente
17
si alguna de las entradas de csv contiene una coma (,) este código no funcionará.
hakan
Para mantener las cosas livianas, usé un carácter de tubería como separador. '|'
Base33
Excelente solución. Solo una pregunta sobre el segundo fragmento. Qué tipo de objeto es Persona
Cocoa Dev
@CocoaDev Es una clase que contiene dos propiedades de cadena: Nombre y Número de teléfono. Puramente por el ejemplo sin embargo. Si alguna de las propiedades fuera un número entero, debería ser solo una conversión directa (¿con verificación?).
Base33
10

Esta solución está utilizando el ensamblado oficial Microsoft.VisualBasic para analizar CSV.

Ventajas:

  • delimitador escapando
  • ignora el encabezado
  • recortar espacios
  • ignorar comentarios

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
fuente
7

He escrito TinyCsvParser para .NET, que es uno de los analizadores .NET más rápidos y altamente configurable para analizar casi cualquier formato CSV.

Se publica bajo licencia MIT:

Puede usar NuGet para instalarlo. Ejecute el siguiente comando en la consola de Package Manager .

PM> Install-Package TinyCsvParser

Uso

Imagine que tenemos una lista de personas en un archivo CSV persons.csvcon su nombre, apellido y fecha de nacimiento.

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

El modelo de dominio correspondiente en nuestro sistema podría verse así.

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

Al usar TinyCsvParser, debe definir la asignación entre las columnas en los datos CSV y la propiedad en su modelo de dominio.

private class CsvPersonMapping : CsvMapping<Person>
{

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

Y luego podemos usar el mapeo para analizar los datos CSV con 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);
        }
    }
}

Guía del usuario

Una guía de usuario completa está disponible en:

bytefish
fuente
1

Aquí está mi implementación 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 comenzó
fuente
1
Esto no trata con los saltos de línea dentro de las cadenas entre comillas que es válido en un archivo CSV.
John Leidegren
Alex, lo que John está tratando de decir es que RFC 4180 ( ietf.org/rfc/rfc4180.txt - Ver la sección 2 y el ítem 6) permite que una columna tenga un CR LF en el medio de una columna que efectivamente se extiende 2 líneas en un archivo. Su solución probablemente funcionará bien en la mayoría de los casos (especialmente si los archivos CSV se crearon al guardarlos desde Excel), pero no cubre este caso extremo. Se supone que CsvHelper, mencionado anteriormente, tiene en cuenta este caso.
David Yates el
Sí, esto es cierto, pero si tiene CR LF en su CSV, es probable que no esté usando CSV, sino algo más apropiado como, json o xml, o formato de longitud fija.
Alex comenzó el
1

Hace algún tiempo escribí una clase simple para lectura / escritura CSV basada en la Microsoft.VisualBasicbiblioteca. Con esta clase simple, podrá trabajar con CSV como con una matriz de 2 dimensiones. Puedes encontrar mi clase en el siguiente enlace: https://github.com/ukushu/DataExporter

Ejemplo simple 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 leer el encabezado solo necesitas leer las csv.Rows[0]celdas :)

Andrés
fuente
1

Solución de archivo de fuente única para necesidades de análisis directo, útil. Se ocupa de todos los casos extremos desagradables. Como la normalización de nuevas líneas y el manejo de nuevas líneas en literales de cadena entre comillas. ¡De nada!

Si su archivo CSV tiene un encabezado, simplemente lea los nombres de columna (y calcule los índices de columna) de la primera fila. Simple como eso.

Tenga en cuenta que Dumpes un método LINQPad, es posible que desee eliminarlo si no está utilizando 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
fuente
1

Otro de esta lista, Cinchoo ETL , una biblioteca de código abierto para leer y escribir múltiples formatos de archivo (CSV, archivo plano, Xml, JSON, etc.)

El siguiente ejemplo muestra cómo leer un archivo CSV rápidamente (no se requiere un objeto POCO)

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}");
    }
}

El siguiente ejemplo muestra cómo leer un archivo CSV usando un 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}");
        }
    }
}

Consulte los artículos en CodeProject sobre cómo usarlo.

RajN
fuente
0

¿Basado en la publicación de Unlimit en Cómo dividir correctamente un CSV usando la función C # split ()? :

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

NOTA: esto no maneja comas escapadas / anidadas, etc., y por lo tanto solo es adecuado para ciertas listas CSV simples.

radsdau
fuente
2
Esto es muy malo y probablemente lento :)
EKS
1
Probablemente, pero funciona perfecta y simplemente para un pequeño conjunto de parámetros, por lo tanto, es una solución válida y útil. ¿Por qué rechazarlo? "Muy malo" es un poco extremo, ¿no te parece?
radsdau
1
No maneja comas escapadas / anidadas, etc. Funcionará en algunos casos pero definitivamente no funcionará para todos los archivos csv
NStuke
Tienes razón; Editaré la respuesta para reflejar eso. Gracias. Pero todavía tiene su lugar.
radsdau
Esto funcionó perfectamente para mi caso de uso, donde estoy construyendo un servidor sql clr dll y no puedo usar ninguno de estos otros paquetes externos. Solo necesitaba analizar un archivo csv simple con un nombre de archivo y un recuento de filas.
dubvfan87
0

Este código lee csv en 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
fuente
1
TextFieldParser está en Microsoft.VisualBasic.dll.
user3285954
0

Si alguien quiere un fragmento, puede introducir su código sin tener que vincular una biblioteca o descargar un paquete. Aquí hay una versión que escribí:

    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;
    }
Juan
fuente
0

Aquí hay una solución corta y simple.

                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);
                        }
                    }
El halcón
fuente