2008-09-22 5 views
14

Dans mon application C#, j'utilise le fournisseur de données Microsoft Jet OLEDB pour lire un fichier CSV. La chaîne de connexion ressemble à ceci:Lors de la lecture d'un fichier CSV à l'aide d'un DataReader et du fournisseur de données OLEDB Jet, comment puis-je contrôler les types de données de colonne?

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

-je ouvrir un OleDbConnection ADO.NET en utilisant cette chaîne de connexion et sélectionnez toutes les lignes du fichier CSV avec la commande:

select * from Data.csv 

Quand j'ouvre un OleDbDataReader et d'examiner les types de données des colonnes qu'il retourne, je trouve que quelque chose dans la pile a essayé de deviner les types de données en fonction de la première ligne de données dans le fichier. Par exemple, supposons que le fichier CSV contient:

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

Appel de la méthode OleDbDataReader.GetDataTypeName pour la colonne Maison révèle que la colonne a été donné le type de données « DBTYPE_I4 », donc toutes les valeurs lues à partir de celui-ci sont interprétées comme entiers. Mon problème est que House doit être une chaîne - quand j'essaye de lire la valeur de House de la deuxième rangée, le OleDbDataReader renvoie la null.

Comment puis-je dire au fournisseur de base de données Jet ou à OleDbDataReader d'interpréter une colonne comme des chaînes plutôt que des nombres?

Répondre

11

développiez la réponse de Marc, je dois créer un fichier texte appelé Schema.ini et le mettre dans le même répertoire que le fichier CSV. En plus des types de colonnes, ce fichier peut spécifier le format de fichier, le format de date et d'heure, les paramètres régionaux et les noms de colonnes s'ils ne sont pas inclus dans le fichier.

Pour l'exemple que je donnais dans les travaux de la question, le fichier de schéma doit ressembler à ceci:

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

Je pourrais aussi essayer cela pour faire le fournisseur de données examiner toutes les lignes dans le fichier avant d'essayer de deviner les types de données:

[Data.csv] 
ColNameHeader=true 
MaxScanRows=0 

Dans la vraie vie, mes données sur les importations d'application à partir de fichiers avec des noms dynamiques, donc je dois créer un fichier Schema.ini à la volée et l'écrire dans le même répertoire que le CSV fichier avant d'ouvrir ma connexion.

Vous trouverez plus de détails ici - http://msdn.microsoft.com/en-us/library/ms709353(VS.85).aspx - ou en recherchant dans la bibliothèque MSDN "fichier Schema.ini".

5

S'il vous plaît vérifier

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

Ceci contournerait complètement le OleDbProvider, ce qui est probablement une bonne chose. Les valeurs renvoyées par 'record [" FieldName "]' sont toutes des chaînes - mon code devrait savoir à l'avance quel type de données est attendu de chaque colonne, et exécuter les chaînes via 'System.Convert'. –

0

Vous devez indiquer au pilote de scanner toutes les lignes pour déterminer le schéma. Sinon, si les premières lignes sont numériques et les autres sont alphanumériques, les cellules alphanumériques seront vides.

Comme Rory, j'ai trouvé que j'avais besoin de créer un fichier schema.ini dynamiquement, car il n'y a aucun moyen de dire au pilote d'analyser toutes les lignes.(Ce n'est pas le cas pour les fichiers Excel)

Vous devez avoir MaxScanRows=0 dans votre schema.ini

Voici un exemple de code:

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

Vous aurez besoin de faire quelques modifications si vous êtes en cours d'exécution sur le même répertoire dans plusieurs threads en même temps.

Questions connexes