C# 使用DataReader和OLEDB Jet数据提供程序读取CSV文件时,如何控制列数据类型?

C# 使用DataReader和OLEDB Jet数据提供程序读取CSV文件时,如何控制列数据类型?,c#,.net,csv,oledb,C#,.net,Csv,Oledb,在我的C应用程序中,我使用Microsoft Jet OLEDB数据提供程序读取CSV文件。连接字符串如下所示: Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\Data;Extended Properties="text;HDR=Yes;FMT=Delimited [Data.csv] ColNameHeader=True Col1=House Text Col2=Street Text Col3=Town Text 我使用该连接字符串打开A

在我的C应用程序中,我使用Microsoft Jet OLEDB数据提供程序读取CSV文件。连接字符串如下所示:

Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\Data;Extended Properties="text;HDR=Yes;FMT=Delimited
[Data.csv]
ColNameHeader=True
Col1=House Text
Col2=Street Text
Col3=Town Text
我使用该连接字符串打开ADO.NET OleDbConnection,并使用以下命令从CSV文件中选择所有行:

select * from Data.csv
当我打开OleDbDataReader并检查它返回的列的数据类型时,我发现堆栈中的某些东西试图根据文件中的第一行数据猜测数据类型。例如,假设CSV文件包含:

House,Street,Town
123,Fake Street,Springfield
12a,Evergreen Terrace,Springfield
为House列调用OleDbDataReader.GetDataTypeName方法将显示该列已被赋予数据类型DBTYPE_I4,因此从中读取的所有值都被解释为整数。我的问题是House应该是一个字符串——当我尝试从第二行读取House值时,OleDbDataReader返回null


我如何告诉Jet数据库提供程序或OleDbDataReader将列解释为字符串而不是数字?

您可以创建一个模式文件,告诉ADO.NET如何解释CSV,实际上是给它一个结构

试试这个:


或者最新的

要扩展Marc的答案,我需要创建一个名为Schema.ini的文本文件,并将其放在与CSV文件相同的目录中。除了列类型之外,此文件还可以指定文件格式、日期时间格式、区域设置以及列名(如果文件中未包含列名)

为了使我在问题中给出的示例起作用,模式文件应该如下所示:

Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\Data;Extended Properties="text;HDR=Yes;FMT=Delimited
[Data.csv]
ColNameHeader=True
Col1=House Text
Col2=Street Text
Col3=Town Text
我还可以尝试这样做,使数据提供程序在尝试猜测数据类型之前检查文件中的所有行:

[Data.csv]
ColNameHeader=true
MaxScanRows=0
在现实生活中,我的应用程序从具有动态名称的文件导入数据,因此我必须动态创建Schema.ini文件,并在打开连接之前将其写入CSV文件所在的目录

更多详细信息可在此处找到,或通过搜索MSDN库中的Schema.ini文件找到。

请检查


您需要告诉驱动程序扫描所有行以确定模式。否则,如果前几行为数字,其余为字母数字,则字母数字单元格将为空

比如,我发现需要动态创建schema.ini文件,因为无法通过编程告诉驱动程序扫描所有行。excel文件并非如此

schema.ini中必须有MaxScanRows=0

下面是一个代码示例:

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

如果同时在多个线程的同一目录下运行此程序,则需要进行一些修改。

这将完全绕过OleDbProvider,这可能是一件好事。从记录[FieldName]返回的值都是字符串-我的代码需要提前知道每个列的预期数据类型,并通过System.Convert运行字符串。