Al leer un archivo CSV utilizando un DataReader y el proveedor de datos OLEDB Jet, ¿cómo puedo controlar los tipos de datos de columna?

En mi aplicación C # estoy usando el proveedor de datos OLEDB de Microsoft Jet para leer un archivo CSV. La cadena de conexión se ve así:

Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\Data;Extended Properties="text;HDR=Yes;FMT=Delimited 

Abrí un OleObConnection de ADO.NET usando esa cadena de conexión y seleccioné todas las filas del archivo CSV con el comando:

 select * from Data.csv 

Cuando abro OleDbDataReader y examino los tipos de datos de las columnas que devuelve, encuentro que algo en la stack ha intentado adivinar los tipos de datos basados ​​en la primera fila de datos en el archivo. Por ejemplo, supongamos que el archivo CSV contiene:

 House,Street,Town 123,Fake Street,Springfield 12a,Evergreen Terrace,Springfield 

Llamar al método OleDbDataReader.GetDataTypeName para la columna House revelará que a la columna se le ha asignado el tipo de datos “DBTYPE_I4”, por lo que todos los valores leídos se interpretan como enteros. Mi problema es que House debería ser una cadena: cuando trato de leer el valor de House desde la segunda fila, OleDbDataReader devuelve null.

¿Cómo puedo decirle al proveedor de la base de datos Jet o al OleDbDataReader que interprete una columna como cadenas en lugar de números?

Hay un archivo de esquema que puede crear que le indicaría a ADO.NET cómo interpretar el CSV, dándole de hecho una estructura.

Pruebe esto: http://www.aspdotnetcodes.com/Importing_CSV_Database_Schema.ini.aspx

Para ampliar la respuesta de Marc, necesito crear un archivo de texto llamado Schema.ini y ponerlo en el mismo directorio que el archivo CSV. Además de los tipos de columna, este archivo puede especificar el formato de archivo, el formato de fecha y hora, la configuración regional y los nombres de columna si no están incluidos en el archivo.

Para hacer que funcione el ejemplo que di en la pregunta, el archivo de esquema debería verse así:

 [Data.csv] ColNameHeader=True Col1=House Text Col2=Street Text Col3=Town Text 

También podría intentar esto para que el proveedor de datos examine todas las filas del archivo antes de intentar adivinar los tipos de datos:

 [Data.csv] ColNameHeader=true MaxScanRows=0 

En la vida real, mi aplicación importa datos de archivos con nombres dynamics, así que tengo que crear un archivo Schema.ini sobre la marcha y escribirlo en el mismo directorio que el archivo CSV antes de abrir mi conexión.

Se pueden encontrar más detalles aquí: http://msdn.microsoft.com/en-us/library/ms709353(VS.85).aspx o buscando en la biblioteca MSDN el “archivo Schema.ini”.

por favor, compruebe

http://kbcsv.codeplex.com/

 using (var reader = new CsvReader("data.csv")) { reader.ReadHeaderRecord(); foreach (var record in reader.DataRecords) { var name = record["Name"]; var age = record["Age"]; } } 

Debe decirle al controlador que explore todas las filas para determinar el esquema. De lo contrario, si las primeras filas son numéricas y el rest son alfanuméricas, las celdas alfanuméricas estarán en blanco.

Al igual que Rory , descubrí que necesitaba crear dinámicamente un archivo schema.ini porque no hay forma de decirle programáticamente al controlador que escanee todas las filas. (este no es el caso para los archivos de Excel)

Debe tener MaxScanRows=0 en su schema.ini

Aquí hay un ejemplo de código:

  public static DataTable GetDataFromCsvFile(string filePath, bool isFirstRowHeader = true) { if (!File.Exists(filePath)) { throw new FileNotFoundException("The path: " + filePath + " doesn't exist!"); } if (!(Path.GetExtension(filePath) ?? string.Empty).ToUpper().Equals(".CSV")) { throw new ArgumentException("Only CSV files are supported"); } var pathOnly = Path.GetDirectoryName(filePath); var filename = Path.GetFileName(filePath); var schemaIni = $"[{filename}]{Environment.NewLine}" + $"Format=CSVDelimited{Environment.NewLine}" + $"ColNameHeader={(isFirstRowHeader ? "True" : "False")}{Environment.NewLine}" + $"MaxScanRows=0{Environment.NewLine}" + $" ; scan all rows for data type{Environment.NewLine}" + $" ; This file was automatically generated"; var schemaFile = pathOnly != null ? Path.Combine(pathOnly, "schema.ini") : "schema.ini"; File.WriteAllText(schemaFile, schemaIni); try { var sqlCommand = $@"SELECT * FROM [{filename}]"; var oleDbConnString = $"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={pathOnly};Extended Properties=\"Text;HDR={(isFirstRowHeader ? "Yes" : "No")}\""; using (var oleDbConnection = new OleDbConnection(oleDbConnString)) using (var adapter = new OleDbDataAdapter(sqlCommand, oleDbConnection)) using (var dataTable = new DataTable()) { adapter.FillSchema(dataTable, SchemaType.Source); adapter.Fill(dataTable); return dataTable; } } finally { if (File.Exists(schemaFile)) { File.Delete(schemaFile); } } } 

Tendrá que hacer algunas modificaciones si está ejecutando esto en el mismo directorio en varios subprocesos al mismo tiempo.