Leer archivos CSV usando C #

Estoy escribiendo una aplicación de importación simple y necesito leer un archivo CSV, mostrar el resultado en un DataGrid y mostrar las líneas dañadas del archivo CSV en otra cuadrícula. Por ejemplo, muestre las líneas que son más cortas que 5 valores en otra grilla. Estoy tratando de hacer eso así:

 StreamReader sr = new StreamReader(FilePath); importingData = new Account(); string line; string[] row = new string [5]; while ((line = sr.ReadLine()) != null) { row = line.Split(','); importingData.Add(new Transaction { Date = DateTime.Parse(row[0]), Reference = row[1], Description = row[2], Amount = decimal.Parse(row[3]), Category = (Category)Enum.Parse(typeof(Category), row[4]) }); } 

pero es muy difícil operar en arrays en este caso. ¿Hay alguna forma mejor de dividir los valores?

No reinventar la rueda. Aproveche lo que ya está en .NET BCL.

  • agregue una referencia al Microsoft.VisualBasic (sí, dice VisualBasic pero también funciona en C #, recuerde que al final todo es IL)
  • utilice la clase Microsoft.VisualBasic.FileIO.TextFieldParser para analizar el archivo CSV

Aquí está el código de ejemplo:

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

Funciona muy bien para mí en mis proyectos de C #.

Aquí hay algunos enlaces más / información:

  • MSDN: leer archivos de texto delimitados por comas en Visual Basic
  • MSDN: clase TextFieldParser

Mi experiencia es que hay muchos formatos de cvs diferentes. Especialmente cómo manejan el escape de citas y delimitadores dentro de un campo.

Estas son las variantes con las que me he encontrado:

  • las comillas se cotizan y duplican (excel), es decir, 15 “-> campo1,” 15 “” “, campo3
  • las cotizaciones no se cambian a menos que el campo se cotice por alguna otra razón. es decir, 15 “-> campo1,15”, campos3
  • las comillas se escapan con \. es decir, 15 “-> campo1,” 15 \ “”, campo3
  • las citas no se cambian en absoluto (esto no siempre es posible analizar correctamente)
  • el delimitador se cita (excel). es decir, a, b -> campo1, “a, b”, campo3
  • delimiter se escapó con \. es decir, a, b -> campo1, a \, b, campo3

He intentado muchos de los analizadores csv existentes, pero no hay ninguno que pueda manejar las variantes con las que me he encontrado. También es difícil averiguar a partir de la documentación qué variantes de escape admiten los analizadores.

En mis proyectos ahora uso VB TextFieldParser o un splitter personalizado.

Recomiendo CsvHelper de Nuget .

(Agregar una referencia a Microsoft.VisualBasic simplemente no parece correcto, no solo es feo, probablemente ni siquiera sea multiplataforma).

En ocasiones, utilizar bibliotecas es genial cuando no se quiere reinventar la rueda, pero en este caso se puede hacer el mismo trabajo con menos líneas de código y más fácil de leer en comparación con el uso de bibliotecas. Aquí hay un enfoque diferente que me resulta muy fácil de usar.

  1. En este ejemplo, uso StreamReader para leer el archivo
  2. Regex para detectar el delimitador de cada línea (s).
  3. Una matriz para recostackr las columnas del índice 0 a n

 using (StreamReader reader = new StreamReader(fileName)) { string line; while ((line = reader.ReadLine()) != null) { //Define pattern Regex CSVParser = new Regex(",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))"); //Separating columns to array string[] X = CSVParser.Split(line); /* Do something with X */ } } 

CSV puede complicarse muy rápido.

Use algo robusto y bien probado:
FileHelpers: http://www.filehelpers.net

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 flujos.

Yo uso esto aquí:

http://www.codeproject.com/KB/database/GenericParser.aspx

La última vez que estaba buscando algo así, lo encontré como una respuesta a esta pregunta .

Otro en esta lista, Cinchoo ETL – una biblioteca de código abierto para leer y escribir archivos CSV

Para un archivo CSV de muestra a continuación

 Id, Name 1, Tom 2, Mark 

Rápidamente puede cargarlos usando la biblioteca de la siguiente manera

 using (var reader = new ChoCSVReader("test.csv").WithFirstLineHeader()) { foreach (dynamic item in reader) { Console.WriteLine(item.Id); Console.WriteLine(item.Name); } } 

Si tiene una clase POCO que coincida con el archivo CSV

 public class Employee { public int Id { get; set; } public string Name { get; set; } } 

Puede usarlo para cargar el archivo CSV como se muestra a continuación

 using (var reader = new ChoCSVReader("test.csv").WithFirstLineHeader()) { foreach (var item in reader) { Console.WriteLine(item.Id); Console.WriteLine(item.Name); } } 

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

Descargo de responsabilidad: soy el autor de esta biblioteca

Antes que nada necesitamos entender qué es CSV y cómo escribirlo.

  1. Cada cadena siguiente ( /r/n ) es la siguiente fila de “tabla”.
  2. Las celdas de “Tabla” están separadas por algún símbolo delimitador. Los símbolos más utilizados son \t or ,
  3. Cada celda posiblemente puede contener este símbolo delimitador (la celda debe comenzar con el símbolo de comillas y termina con este símbolo en este caso)
  4. Cada celda posiblemente puede contener /r/n sybols (la celda debe comenzar con el símbolo de comillas y termina con este símbolo en este caso)

La forma más fácil para que C # / Visual Basic funcione con archivos CSV es utilizar la biblioteca estándar Microsoft.VisualBasic . Solo necesita agregar la referencia necesaria y la siguiente cadena a su clase:

 using Microsoft.VisualBasic.FileIO; 

Sí, puedes usarlo en C #, no te preocupes. Esta biblioteca puede leer archivos relativamente grandes y admite todas las reglas necesarias, por lo que podrá trabajar con todos los archivos CSV.

Hace algún tiempo, escribí una clase simple de CSV de lectura / escritura basada en esta biblioteca. Usando esta clase simple podrás trabajar con CSV como con 2 dimensiones de matriz. 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"); 

Para completar las respuestas anteriores, uno puede necesitar una colección de objetos de su archivo CSV, ya sea analizados por el TextFieldParser o por el método string.Split , y luego cada línea convertida en un objeto a través de Reflection. Obviamente, primero necesita definir una clase que coincida con las líneas del archivo CSV.

Utilicé el Serializador CSV simple de Michael Kropat que se encuentra aquí: Clase genérica a CSV (todas las propiedades) y reutilicé sus métodos para obtener los campos y las propiedades de la clase deseada.

Deserializo mi archivo CSV con el siguiente método:

 public static IEnumerable ReadCsvFileTextFieldParser(string fileFullPath, string delimiter = ";") where T : new() { if (!File.Exists(fileFullPath)) { return null; } var list = new List(); var csvFields = GetAllFieldOfClass(); var fieldDict = new Dictionary(); using (TextFieldParser parser = new TextFieldParser(fileFullPath)) { parser.SetDelimiters(delimiter); bool headerParsed = false; while (!parser.EndOfData) { //Processing row string[] rowFields = parser.ReadFields(); if (!headerParsed) { for (int i = 0; i < rowFields.Length; i++) { // First row shall be the header! var csvField = csvFields.Where(f => f.Name == rowFields[i]).FirstOrDefault(); if (csvField != null) { fieldDict.Add(i, csvField); } } headerParsed = true; } else { T newObj = new T(); for (int i = 0; i < rowFields.Length; i++) { var csvFied = fieldDict[i]; var record = rowFields[i]; if (csvFied is FieldInfo) { ((FieldInfo)csvFied).SetValue(newObj, record); } else if (csvFied is PropertyInfo) { var pi = (PropertyInfo)csvFied; pi.SetValue(newObj, Convert.ChangeType(record, pi.PropertyType), null); } else { throw new Exception("Unhandled case."); } } if (newObj != null) { list.Add(newObj); } } } } return list; } public static IEnumerable GetAllFieldOfClass() { return from mi in typeof(T).GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static) where new[] { MemberTypes.Field, MemberTypes.Property }.Contains(mi.MemberType) let orderAttr = (ColumnOrderAttribute)Attribute.GetCustomAttribute(mi, typeof(ColumnOrderAttribute)) orderby orderAttr == null ? int.MaxValue : orderAttr.Order, mi.Name select mi; } 
 private static DataTable ConvertCSVtoDataTable(string strFilePath) { DataTable dt = new DataTable(); using (StreamReader sr = new StreamReader(strFilePath)) { string[] headers = sr.ReadLine().Split(','); foreach (string header in headers) { dt.Columns.Add(header); } while (!sr.EndOfStream) { string[] rows = sr.ReadLine().Split(','); DataRow dr = dt.NewRow(); for (int i = 0; i < headers.Length; i++) { dr[i] = rows[i]; } dt.Rows.Add(dr); } } return dt; } private static void WriteToDb(DataTable dt) { string connectionString = "Data Source=localhost;" + "Initial Catalog=Northwind;" + "Integrated Security=SSPI;"; using (SqlConnection con = new SqlConnection(connectionString)) { using (SqlCommand cmd = new SqlCommand("spInsertTest", con)) { cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@policyID", SqlDbType.Int).Value = 12; cmd.Parameters.Add("@statecode", SqlDbType.VarChar).Value = "blagh2"; cmd.Parameters.Add("@county", SqlDbType.VarChar).Value = "blagh3"; con.Open(); cmd.ExecuteNonQuery(); } } }