Как при чтении файла CSV с помощью DataReader и поставщика данных OLEDB Jet можно управлять типами данных столбцов?
Вопрос
В моем приложении C# я использую поставщика данных Microsoft Jet OLEDB для чтения файла CSV.Строка подключения выглядит следующим образом:
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\Data;Extended Properties="text;HDR=Yes;FMT=Delimited
Я открываю ADO.NET OleDbConnection, используя эту строку подключения, и выбираю все строки из файла CSV с помощью команды:
select * from Data.csv
Когда я открываю OleDbDataReader и проверяю типы данных столбцов, которые он возвращает, я обнаруживаю, что что-то в стеке попыталось угадать типы данных на основе первой строки данных в файле.Например, предположим, что файл CSV содержит:
House,Street,Town
123,Fake Street,Springfield
12a,Evergreen Terrace,Springfield
Вызов метода OleDbDataReader.GetDataTypeName для столбца House покажет, что столбцу присвоен тип данных «DBTYPE_I4», поэтому все значения, считанные из него, интерпретируются как целые числа.Моя проблема в том, что House должен быть строкой: когда я пытаюсь прочитать значение House из второй строки, OleDbDataReader возвращает значение null.
Как я могу указать поставщику базы данных Jet или OleDbDataReader интерпретировать столбец как строки, а не числа?
Решение
Вы можете создать файл схемы, который сообщит ADO.NET, как интерпретировать CSV, фактически придавая ему структуру.
Попробуй это: http://www.aspdotnetcodes.com/Importing_CSV_Database_Schema.ini.aspx
Другие советы
Чтобы расширить ответ Марка, мне нужно создать текстовый файл с именем Schema.ini и поместить его в тот же каталог, что и файл CSV.Помимо типов столбцов, в этом файле можно указать формат файла, формат даты и времени, региональные настройки и имена столбцов, если они не включены в файл.
Чтобы пример, который я привел в вопросе, работал, файл схемы должен выглядеть следующим образом:
[Data.csv]
ColNameHeader=True
Col1=House Text
Col2=Street Text
Col3=Town Text
Я также мог бы попробовать сделать так, чтобы поставщик данных проверял все строки в файле, прежде чем он попытается угадать типы данных:
[Data.csv]
ColNameHeader=true
MaxScanRows=0
В реальной жизни мое приложение импортирует данные из файлов с динамическими именами, поэтому мне приходится на лету создавать файл Schema.ini и записывать его в тот же каталог, что и файл CSV, прежде чем открывать соединение.
Более подробную информацию можно найти здесь - http://msdn.microsoft.com/en-us/library/ms709353(VS.85).aspx - или выполнив поиск в библиотеке MSDN по запросу «файл Schema.ini».
пожалуйста, проверьте
using (var reader = new CsvReader("data.csv"))
{
reader.ReadHeaderRecord();
foreach (var record in reader.DataRecords)
{
var name = record["Name"];
var age = record["Age"];
}
}
Вам нужно указать драйверу, чтобы он сканировал все строки, чтобы определить схему.В противном случае, если первые несколько строк являются числовыми, а остальные — буквенно-цифровыми, буквенно-цифровые ячейки будут пустыми.
Нравиться Рори, я обнаружил, что мне нужно создать файл Schema.ini динамически, поскольку не существует способа программно указать драйверу сканировать все строки.(это не относится к файлам Excel)
Вы должны иметь MaxScanRows=0
в вашей схеме.ini
Вот пример кода:
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 = $@"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);
}
}
}
Вам нужно будет внести некоторые изменения, если вы запускаете это в одном и том же каталоге в нескольких потоках одновременно.