textfieldparser read parser lumenworks framework example c# csv file-io io header

parser - read csv file c# example



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

¿Existe una forma predeterminada / oficial / recomendada para 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 lo desalientan debido a sus "inconvenientes". ¿Cuáles son estos inconvenientes?

Idealmente, estoy buscando una forma de leer el CSV por nombre de columna, usando el primer registro como nombre de encabezado / campo. Algunas de las respuestas son correctas, pero funcionan para básicamente deserializar el archivo en clases.


¡Deja que una biblioteca se encargue de todos los detalles esenciales para ti! :-)

Eche un vistazo a FileHelpers y manténgase seco. No se repita, no es necesario volver a inventar la rueda una vez más.

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


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


Aquí hay una clase de ayuda que uso con frecuencia, en caso de que alguna vez vuelva a este hilo (quería compartirlo).

Utilizo esto por la simplicidad de portarlo en 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 utilízalo 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; }

[Actualizado csv helper: error corregido donde el último carácter de nueva línea creó una nueva línea]



En una aplicación empresarial, 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.


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

Ventajas:

  • delimitador escapando
  • ignora el encabezado
  • espacios de recorte
  • 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; }


Este código lee csv a 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; }


Hace algún tiempo había escrito una clase simple para lectura / escritura CSV basada en la biblioteca Microsoft.VisualBasic . Usando esta clase simple, podrá trabajar con CSV como con un arreglo de 2 dimensiones. Puede 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");

Solo para leer el encabezado que necesita es leer csv.Rows[0] cells :)


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

Se libera bajo licencia MIT:

Puedes usar NuGet para instalarlo. Ejecute el siguiente comando en la Consola del Administrador de paquetes .

PM> Install-Package TinyCsvParser

Uso

Imagina que tenemos una lista de personas en un archivo CSV persons.csv con 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 tener este aspecto.

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 la asignación para analizar los datos CSV con un 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:


No hay ninguna forma oficial que conozca, pero de hecho debería usar las bibliotecas existentes. Aquí hay uno que encontré realmente útil de CodeProject:

CSVReader


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 el archivo CSV rápidamente (no se requiere ningún objeto POCO)

static void ReadCSV() { using (var stream = new MemoryStream()) using (var reader = new StreamReader(stream)) using (var writer = new StreamWriter(stream)) using (var parser = new ChoCSVReader(reader)) { writer.WriteLine("id,name"); writer.WriteLine("1,Carl"); writer.WriteLine("2,Mark"); writer.WriteLine("3,Tom"); writer.Flush(); stream.Position = 0; foreach (dynamic dr in parser) { Console.WriteLine("Id: {0}, Name: {1}", dr.id, dr.name); } } }

El siguiente ejemplo muestra cómo leer el archivo CSV usando el objeto POCO

public partial class EmployeeRec { public int Id { get; set; } public string Name { get; set; } } static void ReadCSV() { using (var stream = new MemoryStream()) using (var reader = new StreamReader(stream)) using (var writer = new StreamWriter(stream)) using (var parser = new ChoCSVReader<EmployeeRec>(reader)) { writer.WriteLine("id,name"); writer.WriteLine("1,Carl"); writer.WriteLine("2,Mark"); writer.WriteLine("3,Tom"); writer.Flush(); stream.Position = 0; foreach (var dr in parser) { Console.WriteLine("Id: {0}, Name: {1}", dr.id, dr.name); } } }

Por favor, echa un vistazo a los artículos en CodeProject sobre cómo usarlo.


Sé que es un poco tarde, pero acabo de encontrar una biblioteca Microsoft.VisualBasic.FileIO que tiene la clase TextFieldParser para procesar archivos csv.


Si solo necesita leer archivos csv, le recomiendo esta biblioteca: CSVReader
Si también necesita generar archivos csv, use este: FileHelpers

Ambos son gratuitos y de código abierto.


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

Si su archivo CSV tiene un encabezado, acaba de leer los nombres de las columnas (y calcular los índices de las columnas) de la primera fila. Simple como eso.

Tenga en cuenta que Dump es un método LINQPad, es posible que desee eliminarlo si no está usando 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; } }


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


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 que intentas leer. En este caso, puede usar la asignación fluida 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>(); } }