При чтении CSV файла с использованием DataReader и поставщика данных OLEDB Jet, как я могу управлять типами данных столбцов?

В моем приложении С# я использую поставщик данных 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 интерпретирует столбец как строки вместо чисел?

Ответ 1

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

Попробуйте следующее: http://www.aspdotnetcodes.com/Importing_CSV_Database_Schema.ini.aspx

Ответ 2

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

Ответ 3

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

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"];
    }
}

Ответ 4

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

Как Rory, я обнаружил, что мне нужно создать файл 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 = [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);
            }
        }
    }

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