2008-09-22 14 views
14

In meiner C# -Anwendung verwende ich den Microsoft Jet OLEDB-Datenanbieter zum Lesen einer CSV-Datei. Die Verbindungszeichenfolge sieht wie folgt aus:Wie kann ich beim Lesen einer CSV-Datei mit einem DataReader und dem OLEDB Jet-Datenprovider Spaltendatentypen steuern?

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

ich ein ADO.NET-OleDbConnection öffnen Sie die Verbindungszeichenfolge und wählen Sie alle Zeilen aus der CSV mit dem Befehl Datei:

select * from Data.csv 

Wenn ich öffne eine OleDbDataReader und untersuchen Sie die Datentypen der Spalten, die es zurückgibt, finde ich, dass etwas im Stapel versucht hat, die Datentypen basierend auf der ersten Zeile der Daten in der Datei zu erraten. Angenommen, die CSV-Datei enthält:

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

die OleDbDataReader.GetDataTypeName Methode für das Haus Spalte aufrufen wird zeigen, dass die Spalte den Datentyp „DBTYPE_I4“ gegeben wurde, so dass alle von ihm gelesenen Werte interpretiert werden als ganze Zahlen. Mein Problem ist, dass House eine Zeichenfolge sein sollte - wenn ich versuche, den House-Wert aus der zweiten Zeile zu lesen, gibt der OleDbDataReader null zurück.

Wie kann ich entweder dem Jet-Datenbankanbieter oder dem OleDbDataReader mitteilen, dass eine Spalte als Zeichenfolgen anstelle von Zahlen interpretiert werden soll?

Antwort

11

auf Marcs Antwort zu erweitern, muß ich namens Schema.ini eine Textdatei erstellen und sie im selben Verzeichnis wie die CSV-Datei setzen. Diese Datei kann nicht nur Spaltentypen, sondern auch das Dateiformat, das Datumsformat, regionale Einstellungen und die Spaltennamen angeben, wenn sie nicht in der Datei enthalten sind.

Um das Beispiel mache ich in der Frage der Arbeit gab, sollte die Schema-Datei wie folgt aussehen:

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

Ich könnte auch versuchen, diese der Datenanbieter die alle Zeilen in der Datei untersuchen zu machen, bevor es versucht, die Datentypen zu erraten:

[Data.csv] 
ColNameHeader=true 
MaxScanRows=0 

im wirklichen Leben, meine Anwendung importiert Daten aus Dateien mit dynamischen Namen, so habe ich eine Datei Schema.ini on the fly erstellen und sie in das gleiche Verzeichnis wie die CSV schreiben Datei, bevor ich meine Verbindung öffne.

Weitere Details finden Sie hier - http://msdn.microsoft.com/en-us/library/ms709353(VS.85).aspx - oder durch Durchsuchen der MSDN Library nach "Schema.ini-Datei".

5

Bitte überprüfen

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

Dies würde den OleDbProvider insgesamt umgehen, was wahrscheinlich eine gute Sache ist. Die Werte, die von 'record [" FieldName "] zurückgegeben werden, sind alle Zeichenfolgen - mein Code müsste im Voraus wissen, welcher Datentyp von jeder Spalte erwartet wird, und die Zeichenfolgen durch 'System.Convert' ausführen. –

0

Sie müssen die Fahrer sagen, alle Zeilen zu scannen, das Schema zu bestimmen. Andernfalls, wenn die ersten Zeilen numerisch sind und der Rest alphanumerisch ist, sind die alphanumerischen Zellen leer.

Wie Rory, ich fand, dass ich eine schema.ini-Datei dynamisch erstellen musste, da es keine Möglichkeit gibt, dem Treiber programmatisch zu sagen, alle Zeilen zu scannen.(Dies ist nicht der Fall für Excel-Dateien)

Sie MaxScanRows=0 in Ihrem schema.ini haben muss

Hier ist ein Codebeispiel:

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

Sie werden einige Änderungen tun müssen, wenn Sie sind Führen Sie dies auf dem gleichen Verzeichnis in mehreren Threads zur gleichen Zeit.