2008-09-22 15 views
14

W mojej aplikacji C# używam dostawcy danych OLEDB firmy Microsoft Jet do odczytywania pliku CSV. Ciąg połączenia wygląda następująco:Czy podczas odczytywania pliku CSV za pomocą obiektu DataReader i dostawcy danych OLEDB Jet można kontrolować typy danych kolumn?

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

I otworzyć OleDbConnection ADO.NET przy użyciu tego ciągu połączenia i zaznacz wszystkie wiersze z pliku CSV z poleceniem:

select * from Data.csv 

Kiedy otworzyć OleDbDataReader i zbadaj typy danych zwracanych kolumn, stwierdzam, że coś w stosie próbowało odgadnąć typy danych na podstawie pierwszego wiersza danych w pliku. Na przykład, załóżmy, że plik CSV zawiera:

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

Wywołanie metody OleDbDataReader.GetDataTypeName dla kolumny dom będzie ujawniają, że kolumna została podana typ danych „DBTYPE_I4”, więc wszystkie wartości odczytane z nim są interpretowane jako liczby całkowite. Mój problem polega na tym, że House powinien być ciągiem - kiedy spróbuję odczytać wartość House z drugiego rzędu, OleDbDataReader zwróci wartość null.

Jak mogę powiedzieć dostawcy bazy danych Jet lub OleDbDataReader, aby interpretować kolumnę jako ciągi zamiast liczb?

Odpowiedz

11

Aby rozwinąć na odpowiedź Marca, muszę utworzyć plik tekstowy o nazwie Schema.ini i umieścić go w tym samym katalogu co plik CSV. Oprócz typów kolumn ten plik może określać format pliku, format daty, ustawienia regionalne i nazwy kolumn, jeśli nie są one zawarte w pliku.

Aby przykład dałem w pracach zapytania, plik schematu powinna wyglądać następująco:

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

mogę też spróbować tego, aby dostawcy danych zbadać wszystkie wiersze w pliku przed próbą odgadnąć typy danych:

[Data.csv] 
ColNameHeader=true 
MaxScanRows=0 

w prawdziwym życiu, moich danych importuje aplikacji z plików o nazwach dynamicznych, więc muszę utworzyć plik Schema.ini na bieżąco i zapisz go w tym samym katalogu co plik CSV przed otwarciem połączenia.

Więcej informacji można znaleźć tutaj - http://msdn.microsoft.com/en-us/library/ms709353(VS.85).aspx - lub przeszukując bibliotekę MSDN dla "pliku Schema.ini".

5

Proszę sprawdzić

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

To całkowicie pomijałoby OleDbProvider, co jest prawdopodobnie dobrą rzeczą. Wartości zwracane przez 'record [" FieldName "]' są ciągami - mój kod musiałby wcześniej wiedzieć, jakiego typu danych oczekuje się od każdej kolumny, i uruchamiać łańcuchy za pomocą 'System.Convert'. –

0

Trzeba powiedzieć kierowcy, aby skanować wszystkie wiersze do określenia schematu. W przeciwnym razie, jeśli pierwszych kilka wierszy jest numerycznych, a reszta jest alfanumeryczna, komórki alfanumeryczne będą puste.

Podobnie jak Rory, stwierdziłem, że muszę dynamicznie utworzyć plik schema.ini, ponieważ nie ma sposobu, aby programowo nakazać sterownikowi skanowanie wszystkich wierszy.(To nie jest sprawa dla plików Excel)

Musisz mieć MaxScanRows=0 w schema.ini

Oto przykładowy kod:

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

Musisz zrobić kilka modyfikacji, jeśli są uruchamianie tego w tym samym katalogu w wielu wątkach w tym samym czasie.