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?
Question
Dans mon application C #, j'utilise le fournisseur de données OLEDB Microsoft Jet pour lire un fichier CSV. La chaîne de connexion ressemble à ceci:
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\Data;Extended Properties="text;HDR=Yes;FMT=Delimited
J'ouvre une connexion ADO.NET OleDbConnection à l'aide de cette chaîne de connexion et sélectionne toutes les lignes du fichier CSV à l'aide de la commande:
select * from Data.csv
Lorsque j'ouvre un OleDbDataReader et examine les types de données des colonnes renvoyées, un élément de la pile a tenté de deviner les types de données en fonction de la première ligne de données du fichier. Par exemple, supposons que le fichier CSV contienne:
House,Street,Town
123,Fake Street,Springfield
12a,Evergreen Terrace,Springfield
L'appel de la méthode OleDbDataReader.GetDataTypeName pour la colonne Maison indique que la colonne a reçu le type de données "DBTYPE_I4", de sorte que toutes les valeurs qui y sont lues sont interprétées comme des entiers. Mon problème est que House doit être une chaîne. Lorsque je tente de lire la valeur de House à partir de la deuxième ligne, OleDbDataReader renvoie la valeur null.
Comment puis-je demander au fournisseur de base de données Jet ou à OleDbDataReader d'interpréter une colonne sous forme de chaînes au lieu de nombres?
La solution
Vous pouvez créer un fichier de schéma indiquant à ADO.NET comment interpréter le fichier CSV - en lui donnant une structure.
Essayez ceci: http://www.aspdotnetcodes.com/Importing_CSV_Database_Schema.ini.xx. a>
Autres conseils
Pour développer la réponse de Marc, je dois créer un fichier texte appelé Schema.ini et le placer dans le même répertoire que le fichier CSV. Outre les types de colonne, ce fichier peut spécifier le format de fichier, le format de date / heure, les paramètres régionaux et les noms de colonne s'ils ne sont pas inclus dans le fichier.
Pour que l'exemple que j'ai donné dans la question fonctionne, le fichier de schéma doit ressembler à ceci:
[Data.csv]
ColNameHeader=True
Col1=House Text
Col2=Street Text
Col3=Town Text
Je pourrais également essayer de faire en sorte que le fournisseur de données examine toutes les lignes du fichier avant d'essayer de deviner les types de données:
[Data.csv]
ColNameHeader=true
MaxScanRows=0
Dans la réalité, mon application importe les données à partir de fichiers portant des noms dynamiques. Je dois donc créer un fichier Schema.ini à la volée et l'écrire dans le même répertoire que le fichier CSV avant d'ouvrir ma connexion.
Vous trouverez plus de détails à ce sujet ici - http: // msdn.microsoft.com/en-us/library/ms709353(VS.85).aspx - ou en recherchant le fichier "Schema.ini" dans la bibliothèque MSDN.
Veuillez vérifier
using (var reader = new CsvReader("data.csv"))
{
reader.ReadHeaderRecord();
foreach (var record in reader.DataRecords)
{
var name = record["Name"];
var age = record["Age"];
}
}
Vous devez indiquer au pilote d'analyser toutes les lignes pour déterminer le schéma. Sinon, si les premières lignes sont numériques et les autres alphanumériques, les cellules alphanumériques sont vides.
Comme Rory , j'ai constaté que je devais créer un fichier schema.ini de manière dynamique car il n'y avait aucun moyen de le programmer. Indiquez 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 =
<*>quot;[{filename}]{Environment.NewLine}" +
<*>quot;Format=CSVDelimited{Environment.NewLine}" +
<*>quot;ColNameHeader={(isFirstRowHeader ? "True" : "False")}{Environment.NewLine}" +
<*>quot;MaxScanRows=0{Environment.NewLine}" +
<*>quot; ; scan all rows for data type{Environment.NewLine}" +
<*>quot; ; 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 =
<*>quot;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 exécutez ceci sur le même répertoire dans plusieurs threads en même temps.