2008-09-22 12 views
14

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 es el siguiente: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?

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

abro una OleDbConnection ADO.NET utilizando esa cadena de conexión y seleccionar todas las filas del archivo CSV con el comando:

select * from Data.csv 

Cuando abro una OleDbDataReader y examinar los tipos de datos de las columnas que devuelve, encuentro que algo en la pila 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 

Al llamar al método OleDbDataReader.GetDataTypeName para la columna de la casa va a revelar que la columna se le ha dado el tipo de datos "DBTYPE_I4", por lo que todos los valores leídos desde que 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?

Respuesta

11

Para ampliar la respuesta de Marc, tengo que crear un archivo de texto denominado 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 el ejemplo que he dado en el trabajo de la pregunta, el archivo de esquema debería tener este aspecto:

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

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

[Data.csv] 
ColNameHeader=true 
MaxScanRows=0 

en la vida real, mis datos de las importaciones de aplicación a partir de archivos con nombres dinámicos, así que tengo que crear un archivo Schema.ini sobre la marcha y escribirla en el mismo directorio que el CSV archivo antes de abrir mi conexión.

Más detalles se pueden encontrar aquí - http://msdn.microsoft.com/en-us/library/ms709353(VS.85).aspx - o buscando en la biblioteca de MSDN "archivo Schema.ini".

5

Por favor, comprueba

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"]; 
    } 
} 
+0

Esto omitiría el OleDbProvider por completo, lo que probablemente sea algo bueno. Los valores devueltos por 'record [" FieldName "]' son todas cadenas: mi código necesitaría saber de antemano qué tipo de datos se espera de cada columna, y ejecutar las cadenas a través de 'System.Convert'. –

0

Es necesario decirle al conductor a escanear todas las filas para determinar el esquema. De lo contrario, si las primeras filas son numéricas y el resto son alfanuméricas, las celdas alfanuméricas estarán en blanco.

Como Rory, encontré que necesitaba crear un archivo schema.ini dinámicamente porque no hay forma de decirle programáticamente al controlador que escanee todas las filas.(Esto no es el caso de los archivos de Excel)

Debe tener MaxScanRows=0 en su schema.ini

Aquí está 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 = [email protected]"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); 
      } 
     } 
    } 

Usted tendrá que hacer algunas modificaciones si está ejecutando esto en el mismo directorio en múltiples hilos al mismo tiempo.

Cuestiones relacionadas