Как я могу контролировать типы данных столбца при чтении файла CSV с использованием DataReader и поставщика данных OLEDB Jet? - PullRequest
14 голосов
/ 22 сентября 2008

В своем приложении 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 для столбца «Дом» покажет, что столбцу присвоен тип данных «DBTYPE_I4», поэтому все значения, считанные из него, интерпретируются как целые числа. Моя проблема в том, что House должен быть строкой - когда я пытаюсь прочитать значение House из второй строки, OleDbDataReader возвращает null.

Как я могу сказать либо поставщику базы данных Jet, либо OleDbDataReader интерпретировать столбец как строки вместо чисел?

Ответы [ 4 ]

11 голосов
/ 22 сентября 2008

Чтобы расширить ответ Марка, мне нужно создать текстовый файл с именем 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".

9 голосов
/ 22 сентября 2008

Вы можете создать файл схемы, который скажет ADO.NET, как интерпретировать CSV - фактически придав ему структуру.

Попробуйте это: http://www.aspdotnetcodes.com/Importing_CSV_Database_Schema.ini.aspx

5 голосов
/ 05 октября 2011

Пожалуйста, проверьте

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 голосов
/ 30 ноября 2017

Вы должны указать драйверу сканировать все строки, чтобы определить схему. В противном случае, если первые несколько строк являются числовыми, а остальные - буквенно-цифровыми, буквенно-цифровые ячейки будут пустыми.

Как и Рори , я обнаружил, что мне нужно создать файл schema.ini динамически, потому что нет способа программно сказать драйверу сканировать все строки. (это не относится к файлам Excel)

Вы должны иметь MaxScanRows=0 в вашем schema.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);
            }
        }
    }

Вам нужно будет внести некоторые изменения, если вы выполняете это в одном и том же каталоге в нескольких потоках одновременно.

...