【问题标题】:Import-Csv of huge (13M Rows) filtered using Where-Object with 11k rows使用 11k 行的 Where-Object 过滤的巨大(13M 行)的 Import-Csv
【发布时间】:2018-11-02 13:50:25
【问题描述】:

我有一个巨大的 csv 文件,其中包含大约 1300 万行和大约 50 列(文件 #1)。我有另一个包含大约 11k 行的文件,它是 IP 地址列表(文件 #2),它也是第一个文件中的 50 列之一。如何过滤文件 #1,以便输出仅包含从文件 #2 中找到 IP 地址的那些行?

这是我迄今为止尝试过的,但它已经运行了 12 个小时并且还在计数:

$IP = Get-Content -Path C:\Documents\File2.txt

Import-Csv C:\Documents\File1.csv | Where-Object {$_.IP -eq $IP} | Export-csv -Path C:\Documents\File3.csv -NoTypeInformation

【问题讨论】:

  • Get-Content 返回一个字符串数组。如果要检查$_.IP 是否是该数组中包含的值,请使用-in 而不是-eq,或者使用Where-Object { $IP -contains $_.$IP }。有了这么大的文件,代码会花点时间。也许从 File2.txt 中对 ip 数组进行排序有助于加快速度?
  • 可能值得从您的文件中抽取一小部分样本,以确保在处理这么多数据之前让逻辑正常工作。确定它是否正在工作或现在会更快。
  • +1 @Theo 的评论。而且... 13m x 11k = 143,000,000,000。这似乎达到了“崩溃 ps”的水平......也许可以尝试这样的事情:spjeff.com/2017/06/02/powershell-split-csv-in-1000-line-batches 将您的 csv 分解为一些更易于管理的大小。也许至少你可以运行 13x 1m 的文件。然后在事后附加它们。如果你们认为这是个好主意,我可以写一篇文章。
  • 他正在“流式传输” csv,因此他不会将所有内容都保存在内存中。如果他使用的是 foreach 循环,情况会有所不同。不过,这并不意味着这种方式会很快。 :-)
  • 我没有意识到这是@MikeShepard 的工作原理。我想这是将所有内容都保留在管道中而不是一直分配的主要原因?

标签: powershell csv


【解决方案1】:

你可以让这个脚本运行得更快:

  • 无需使用 Import-Csv/Export-csv。读/写行是 足够而且更快
  • $_.ip -in $IP 效率低下。利用 用于查找的哈希表(它将是即时的)
  • 使用 .net 工具 而不是内置的 cmdlet

下面是我提到的优化脚本。运行前检查注释行

$inFile = "C:\stack\IpTables\Data.txt"
$IPfile =  "C:\stack\IpTables\IPs.txt"
$outFile = "C:\stack\IpTables\OutData.txt"
$ipIndexInData = 47 #index of IP address column in your data file

#build a hashtable for IP look up. 
$hash = @{}
[System.IO.File]::ReadAllLines($IPfile) | foreach {$hash.Add($_, $true)}
# if IP values in your list are not unique then wrap $hash.Add() with try/catch


$fsIn = [System.IO.StreamReader]$inFile
$fsOut = [System.IO.StreamWriter]$outFile

$fsOut.WriteLine($fsIn.ReadLine()) # this will write first row with column names to out file. Comment it out if first row is data row

while (!$fsIn.EndOfStream) {

 $line = $fsIn.ReadLine()
 $row = $line -split ","

 if($hash[$row[$ipIndexInData].Trim('"')]) { # remove .Trim('"') if values in your data file are not quoted with "

    $fsOut.WriteLine($line)
 }

}

【讨论】:

  • 我也会这样做:使用 system.io 逐行读取文件并将内容放入哈希表中,因为查找速度非常快。
  • 另一个考虑提高性能的 .net 工具:LINQ
  • 这部分 $row = $line -split "," 如果 csv 中的数据在带引号的字段中有逗号,并且我们不知道其余 (50+) 列中的内容是什么,那么这部分可能会被严重分解。你需要这样的东西:$row = $line -split '\s*,\s*(?!(?<=(?:^|,)\s*"(?:[^"]|""|\\")*,\s*)(?:[^"]|""|\\")*"\s*(?:,|$))'。这会拆分逗号分隔的可选引号字符串列表。它处理引号分隔符"," 和转义引号\"。引号内的空格被保留,外部被吃掉。
  • 另一种快速查找值的方法是将 IP 地址读入 Arraylist $IPlist = [System.Collections.ArrayList](Get-Content "C:\Documents\File2.txt"),然后使用 ArrayList.BinarySearch Method 查找值。这需要对 IP 地址列表进行排序。不知道它是否可以与使用哈希表竞争..
猜你喜欢
  • 2012-06-20
  • 2018-06-03
  • 2021-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-23
  • 2015-02-26
相关资源
最近更新 更多