reading - read csv file c# example
Importe el archivo CSV a una estructura de datos fuertemente tipada en.Net (12)
¿Cuál es la mejor manera de importar un archivo CSV en una estructura de datos fuertemente tipada?
Brian ofrece una buena solución para convertirlo en una colección fuertemente tipada.
La mayoría de los métodos de análisis CSV dados no tienen en cuenta los campos de escape o algunas de las otras sutilezas de los archivos CSV (como los campos de recorte). Aquí está el código que uso personalmente. Es un poco irregular y prácticamente no tiene informes de errores.
public static IList<IList<string>> Parse(string content)
{
IList<IList<string>> records = new List<IList<string>>();
StringReader stringReader = new StringReader(content);
bool inQoutedString = false;
IList<string> record = new List<string>();
StringBuilder fieldBuilder = new StringBuilder();
while (stringReader.Peek() != -1)
{
char readChar = (char)stringReader.Read();
if (readChar == ''/n'' || (readChar == ''/r'' && stringReader.Peek() == ''/n''))
{
// If it''s a /r/n combo consume the /n part and throw it away.
if (readChar == ''/r'')
{
stringReader.Read();
}
if (inQoutedString)
{
if (readChar == ''/r'')
{
fieldBuilder.Append(''/r'');
}
fieldBuilder.Append(''/n'');
}
else
{
record.Add(fieldBuilder.ToString().TrimEnd());
fieldBuilder = new StringBuilder();
records.Add(record);
record = new List<string>();
inQoutedString = false;
}
}
else if (fieldBuilder.Length == 0 && !inQoutedString)
{
if (char.IsWhiteSpace(readChar))
{
// Ignore leading whitespace
}
else if (readChar == ''"'')
{
inQoutedString = true;
}
else if (readChar == '','')
{
record.Add(fieldBuilder.ToString().TrimEnd());
fieldBuilder = new StringBuilder();
}
else
{
fieldBuilder.Append(readChar);
}
}
else if (readChar == '','')
{
if (inQoutedString)
{
fieldBuilder.Append('','');
}
else
{
record.Add(fieldBuilder.ToString().TrimEnd());
fieldBuilder = new StringBuilder();
}
}
else if (readChar == ''"'')
{
if (inQoutedString)
{
if (stringReader.Peek() == ''"'')
{
stringReader.Read();
fieldBuilder.Append(''"'');
}
else
{
inQoutedString = false;
}
}
else
{
fieldBuilder.Append(readChar);
}
}
else
{
fieldBuilder.Append(readChar);
}
}
record.Add(fieldBuilder.ToString().TrimEnd());
records.Add(record);
return records;
}
Tenga en cuenta que esto no maneja el caso de borde de los campos que no están delimitados por comillas dobles, pero meerley tiene una cadena entre comillas dentro de él. Vea esta publicación para una mejor expansión, así como algunos enlaces a algunas bibliotecas adecuadas.
Echa un vistazo a FileHelpers Open Source Library .
Escribí un código. El resultado en el datagridviewer se veía bien. Analiza una sola línea de texto en una matriz de objetos.
enum quotestatus
{
none,
firstquote,
secondquote
}
public static System.Collections.ArrayList Parse(string line,string delimiter)
{
System.Collections.ArrayList ar = new System.Collections.ArrayList();
StringBuilder field = new StringBuilder();
quotestatus status = quotestatus.none;
foreach (char ch in line.ToCharArray())
{
string chOmsch = "char";
if (ch == Convert.ToChar(delimiter))
{
if (status== quotestatus.firstquote)
{
chOmsch = "char";
}
else
{
chOmsch = "delimiter";
}
}
if (ch == Convert.ToChar(34))
{
chOmsch = "quotes";
if (status == quotestatus.firstquote)
{
status = quotestatus.secondquote;
}
if (status == quotestatus.none )
{
status = quotestatus.firstquote;
}
}
switch (chOmsch)
{
case "char":
field.Append(ch);
break;
case "delimiter":
ar.Add(field.ToString());
field.Clear();
break;
case "quotes":
if (status==quotestatus.firstquote)
{
field.Clear();
}
if (status== quotestatus.secondquote)
{
status =quotestatus.none;
}
break;
}
}
if (field.Length != 0)
{
ar.Add(field.ToString());
}
return ar;
}
Estaba aburrido, así que modifiqué algunas cosas que escribí. Intenta encapsular el análisis de una manera OO mientras reduce la cantidad de iteraciones a través del archivo, solo itera una vez en el foreach superior.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// usage:
// note this wont run as getting streams is not Implemented
// but will get you started
CSVFileParser fileParser = new CSVFileParser();
// TO Do: configure fileparser
PersonParser personParser = new PersonParser(fileParser);
List<Person> persons = new List<Person>();
// if the file is large and there is a good way to limit
// without having to reparse the whole file you can use a
// linq query if you desire
foreach (Person person in personParser.GetPersons())
{
persons.Add(person);
}
// now we have a list of Person objects
}
}
public abstract class CSVParser
{
protected String[] deliniators = { "," };
protected internal IEnumerable<String[]> GetRecords()
{
Stream stream = GetStream();
StreamReader reader = new StreamReader(stream);
String[] aRecord;
while (!reader.EndOfStream)
{
aRecord = reader.ReadLine().Split(deliniators,
StringSplitOptions.None);
yield return aRecord;
}
}
protected abstract Stream GetStream();
}
public class CSVFileParser : CSVParser
{
// to do: add logic to get a stream from a file
protected override Stream GetStream()
{
throw new NotImplementedException();
}
}
public class CSVWebParser : CSVParser
{
// to do: add logic to get a stream from a web request
protected override Stream GetStream()
{
throw new NotImplementedException();
}
}
public class Person
{
public String Name { get; set; }
public String Address { get; set; }
public DateTime DOB { get; set; }
}
public class PersonParser
{
public PersonParser(CSVParser parser)
{
this.Parser = parser;
}
public CSVParser Parser { get; set; }
public IEnumerable<Person> GetPersons()
{
foreach (String[] record in this.Parser.GetRecords())
{
yield return new Person()
{
Name = record[0],
Address = record[1],
DOB = DateTime.Parse(record[2]),
};
}
}
}
}
Estoy de acuerdo con @ . FileHelpers está bien probado y maneja todo tipo de casos FileHelpers que eventualmente tendrá que lidiar si lo hace usted mismo. Eche un vistazo a lo que hace FileHelpers y solo escriba el suyo si está absolutamente seguro de que (1) nunca necesitará manejar los casos extremos que FileHelpers hace, o (2) le encanta escribir este tipo de cosas y va a alégrate cuando tengas que analizar cosas como esta:
1, "Bill", "Smith", "Supervisor", "Sin comentarios"
2, ''Drake'', ''O''Malley'', "Conserje,
¡Vaya, no me citan y estoy en una nueva línea!
Hay dos artículos en CodeProject que proporcionan código para una solución, uno que usa CodeProject y otro que importa datos CSV usando el controlador de texto de Microsoft .
Si espera escenarios bastante complejos para el análisis CSV, ni se le ocurra inventar nuestro propio analizador . Existen muchas herramientas excelentes, como FileHelpers o incluso CodeProject .
El punto es que este es un problema bastante común y podría apostar que muchos desarrolladores de software ya han pensado y resuelto este problema.
Si puede garantizar que no hay comas en los datos, entonces la forma más sencilla sería utilizar String.split .
Por ejemplo:
String[] values = myString.Split('','');
myObject.StringField = values[0];
myObject.IntField = Int32.Parse(values[1]);
Puede haber bibliotecas que podría usar para ayudar, pero eso es probablemente lo más simple que puede obtener. Solo asegúrese de que no puede haber comas en los datos, de lo contrario, deberá analizarlos mejor.
Tuve que usar un analizador CSV en .NET para un proyecto este verano y me decidí por el controlador de texto Jet de Microsoft. Usted especifica una carpeta usando una cadena de conexión, luego consulta un archivo usando una instrucción SQL Select. Puede especificar tipos fuertes utilizando un archivo schema.ini. Al principio no hice esto, pero luego obtuve malos resultados donde el tipo de datos no era evidente de inmediato, como números de IP o una entrada como "XYQ 3.9 SP1".
Una limitación con la que me topé es que no puede manejar nombres de columna de más de 64 caracteres; se trunca Esto no debería ser un problema, excepto que estaba tratando con datos de entrada muy mal diseñados. Devuelve un conjunto de datos ADO.NET.
Esta fue la mejor solución que encontré. Sería cauteloso de lanzar mi propio analizador CSV, ya que probablemente me perdería algunos de los casos finales, y no encontré ningún otro paquete de análisis CSV gratuito para .NET.
EDITAR: Además, solo puede haber un archivo schema.ini por directorio, por lo que lo agregué dinámicamente para escribir fuertemente las columnas necesarias. Solo escribirá fuertemente las columnas especificadas e inferirá para cualquier campo no especificado. Realmente aprecié esto, ya que estaba tratando de importar un CSV fluido de más de 70 columnas y no quería especificar cada columna, solo las que se portaban mal.
Una buena manera simple de hacerlo es abrir el archivo y leer cada línea en una matriz, lista vinculada, estructura de datos de su elección. Sin embargo, tenga cuidado al manejar la primera línea.
Esto puede estar sobre su cabeza, pero parece que también hay una forma directa de acceder a ellos usando una cadena de conexión .
¿Por qué no intentar usar Python en lugar de C # o VB? Tiene un buen módulo CSV para importar que hace todo el trabajo pesado por usted.
Use una conexión OleDB.
String sConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C://InputDirectory//;Extended Properties=''text;HDR=Yes;FMT=Delimited''";
OleDbConnection objConn = new OleDbConnection(sConnectionString);
objConn.Open();
DataTable dt = new DataTable();
OleDbCommand objCmdSelect = new OleDbCommand("SELECT * FROM file.csv", objConn);
OleDbDataAdapter objAdapter1 = new OleDbDataAdapter();
objAdapter1.SelectCommand = objCmdSelect;
objAdapter1.Fill(dt);
objConn.Close();
TextFieldParser de Microsoft es estable y sigue RFC 4180 para archivos CSV. No se deje intimidar por el espacio de nombres Microsoft.VisualBasic
; es un componente estándar en .NET Framework, solo agregue una referencia al ensamblado global Microsoft.VisualBasic
.
Si está compilando para Windows (en lugar de Mono) y no anticipa tener que analizar archivos CSV "rotos" (no compatibles con RFC), entonces esta sería la opción obvia, ya que es gratis, sin restricciones, estable, y apoyado activamente, la mayoría de los cuales no se puede decir de FileHelpers.
Vea también: Cómo: Leer desde archivos de texto delimitados por comas en Visual Basic para ver un ejemplo de código VB.