使用 Microsoft.ACE.OLEDB.12.0 OleDbConnection 将 CSV 转换为自定义对象

Converting CSV to custom object using a Microsoft.ACE.OLEDB.12.0 OleDbConnection

我正在尝试读取包含数百万行的大型 CSV 文件以进行测试。我知道我可以使用提供程序 Microsoft.ACE.OLEDB.12.0

将 CSV 视为数据库

使用小型数据集,我可以使用 .GetValue(int) 定位读取行内容。我很难找到更好的方法来读取数据(假设甚至有一个。)。如果我事先知道列名,这很容易。但是,如果我不认识他们,我将不得不阅读文件的第一行以获取看起来很愚蠢的数据。

@"
id,first_name,last_name,email,ip_address
1,Edward,Richards,erichards0@businessweek.com,201.133.112.30
2,Jimmy,Scott,jscott1@clickbank.net,103.231.149.144
3,Marilyn,Williams,mwilliams2@chicagotribune.com,52.180.157.43
4,Frank,Morales,fmorales3@google.ru,218.175.165.205
5,Chris,Watson,cwatson4@ed.gov,75.251.1.149
6,Albert,Ross,aross5@abc.net.au,89.56.133.54
7,Diane,Daniels,ddaniels6@washingtonpost.com,197.156.129.45
8,Nancy,Carter,ncarter7@surveymonkey.com,75.162.65.142
9,John,Kennedy,jkennedy8@tumblr.com,85.35.177.235
10,Bonnie,Bradley,bbradley9@dagondesign.com,255.67.106.193
"@ | Set-Content .\test.csv 

$conn = New-Object System.Data.OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source='C:\Users\Matt';Extended Properties='Text;HDR=Yes;FMT=Delimited';")
$cmd=$conn.CreateCommand()
$cmd.CommandText="Select * from test.csv where first_name like '%n%'"
$conn.open()
$data = $cmd.ExecuteReader()

$data | ForEach-Object{
    [pscustomobject]@{
        id=$_.GetValue(0) 
        first_name=$_.GetValue(1) 
        last_name=$_.GetValue(2)
        ip_address=$_.GetValue(4)
    }
}

$cmd.Dispose()
$conn.Dispose()

是否有更好的方法来处理 $cmd.ExecuteReader() 的输出?很难找到 CSV 导入的信息。大多数 Web 处理使用此提供程序从 SQL 数据库导出到 CSV。这里的逻辑将应用于大型 CSV,这样我就不需要为了忽略大部分数据而阅读整个内容。

我应该仔细看看 TechNet for the OleDbDataReader Class。有一些方法和属性有助于理解从 SQL 语句返回的数据。

  • FieldCount: Gets the number of columns in the current row.

    因此,如果不出意外,您知道您的行有多少列。

  • Item[Int32]: Gets the value of the specified column in its native format given the column ordinal.

    我可以用它从每一行中提取数据。这似乎与 GetValue().

    相同
  • GetName(Int32): Gets the name of the specified column.

    因此,如果您不知道该列的名称,您可以使用它从给定索引中获取它。

还有许多其他方法和一些属性,但如果您不确定 csv 中包含哪些数据(假设您不想事先手动验证),这些方法和属性足以说明问题。所以,知道这一点,获取相同信息的更动态的方式是...

$data | ForEach-Object{

    # Save the current row as its own object so that it can be used in other scopes
    $dataRow = $_
    # Blank hashtable that will be built into a "row" object
    $properties = @{}

    # For every field that exists we will add it name and value to the hashtable
    0..($dataRow.FieldCount - 1) | ForEach-Object{
        $properties.($dataRow.GetName($_)) = $dataRow.Item($_)
    }

    # Send the newly created object down the pipeline.
    [pscustomobject]$properties
}

$cmd.Dispose()
$conn.Dispose()

唯一的缺点是列的输出顺序可能与原始 CSV 的顺序不同。这可以通过将行名称保存在单独的变量中并在管道末尾使用 Select 来解决。这个答案主要是试图理解返回的列名和值。