Windows 来自大文件的共享且唯一的行。最快的方法?
此代码返回两个文件之间的唯一和共享行。不幸的是,如果文件有一百万行,它将永远运行。是否有更快的方法来实现这一点,例如,或者安全壳操作员是最佳方法Windows 来自大文件的共享且唯一的行。最快的方法?,windows,list,powershell,text,Windows,List,Powershell,Text,此代码返回两个文件之间的唯一和共享行。不幸的是,如果文件有一百万行,它将永远运行。是否有更快的方法来实现这一点,例如,或者安全壳操作员是最佳方法 $afile = Get-Content (Read-Host "Enter 'A' file") $bfile = Get-Content (Read-Host "Enter 'B' file") $afile | ? { $bfile -notcontains $_ } | Set-Content lines_ONLY_in_A.txt
$afile = Get-Content (Read-Host "Enter 'A' file")
$bfile = Get-Content (Read-Host "Enter 'B' file")
$afile |
? { $bfile -notcontains $_ } |
Set-Content lines_ONLY_in_A.txt
$bfile |
? { $afile -notcontains $_ } |
Set-Content lines_ONLY_in_B.txt
$afile |
? { $bfile -contains $_ } |
Set-Content lines_in_BOTH_A_and_B.txt
试试这个:
$All=@()
$All+= Get-Content "c:\temp\a.txt" | %{[pscustomobject]@{Row=$_;File="A"}}
$All+= Get-Content "c:\temp\b.txt" | %{[pscustomobject]@{Row=$_;File="B"}}
$All | group row | %{
$InA=$_.Group.File.Contains("A")
$InB=$_.Group.File.Contains("B")
if ($InA -and $InB)
{
$_.Group.Row | select -unique | Out-File c:\temp\lines_in_A_And_B.txt -Append
}
elseif ($InA)
{
$_.Group.Row | select -unique | Out-File c:\temp\lines_Only_A.txt -Append
}
else
{
$_.Group.Row | select -unique | Out-File c:\temp\lines_Only_B.txt -Append
}
}
正如我在回答您前面的一个问题时提到的,-contains是一个缓慢的操作,尤其是对于大型阵列 对于精确匹配,您可以使用Compare Object(比较对象)并按边指示器区分输出:
Compare-Object $afile $bfile -IncludeEqual | ForEach-Object {
switch ($_.SideIndicator) {
'<=' { $_.InputObject | Add-Content 'lines_ONLY_in_A.txt' }
'=>' { $_.InputObject | Add-Content 'lines_ONLY_in_B.txt' }
'==' { $_.InputObject | Add-Content 'lines_in_BOTH_A_and_B.txt' }
}
}
并按如下方式处理文件:
$afile | Where-Object {
-not $bhash.ContainsKey($_)
} | Set-Content 'lines_ONLY_in_A.txt'
如果这仍然不能帮助您确定读取文件、比较数据、进行多重比较的瓶颈。。。然后从这里开始。考虑到我的建议,我已经为此创建了一个可重用的搜索函数: 描述 搜索排序Darray alias Search binary在排序数组中搜索字符串。如果找到字符串,则返回数组中找到的字符串的索引。否则,如果未找到字符串,则返回$Null
Function Search-SortedArray ([String[]]$SortedArray, [String]$Find, [Switch]$CaseSensitive) {
$l = 0; $r = $SortedArray.Count - 1
While ($l -le $r) {
$m = [int](($l + $r) / 2)
Switch ([String]::Compare($find, $SortedArray[$m], !$CaseSensitive)) {
-1 {$r = $m - 1}
1 {$l = $m + 1}
Default {Return $m}
}
}
}; Set-Alias Search Search-SortedArray
$afile |
? {(Search $bfile $_) -eq $Null} |
Set-Content lines_ONLY_in_A.txt
$bfile |
? {(Search $afile $_) -eq $Null} |
Set-Content lines_ONLY_in_B.txt
$afile |
? {(Search $bfile $_) -ne $Null} |
Set-Content lines_in_BOTH_A_and_B.txt
注1:由于开销,二进制搜索只能在非常大的数组中发挥优势
注意2:必须对数组进行排序,否则结果将不可预测
内特3:搜索没有发现重复的。在重复值的情况下,只返回一个索引,这与此特定问题无关
根据@Ansgar Wiechers的评论,增加了2017-11-07:
快速基准测试2个文件,每个文件有几千行,包括重复行:二进制搜索:2400ms;比较对象:1850ms;哈希表查找:250ms 其想法是,从长远来看,阵列将发挥其优势:阵列越大,其性能增益越高 拿了$afile |?{$bfile-notcontains$}例如,注释中的性能度量值和“几千行”是3000行: 对于标准搜索,在$B文件中平均需要1500次迭代:*1 在这两种情况下,对$afile中的每个项目执行此操作3000次 这意味着每次迭代需要: 对于标准搜索:250ms/1500/3000=56纳秒 对于二进制搜索:2400ms/6.27/3000=127482纳秒 盈亏平衡点大约为: 56 * ((x + 1) / 2 * 3000) = 127482 * ((log2 x + 1) / 2 * 3000) 根据我的计算,大约有40000个条目 *1假定哈希表查找本身不进行二进制搜索,因为它不知道数组已排序 2017年11月7日增补
从评论中得出的结论:哈希表似乎有一个类似的算法,在低级编程命令中无法超越 最佳选项的完整代码@ansgar wiechers。A唯一、B唯一和A、B共享行:
$afile = Get-Content (Read-Host "Enter 'A' file")
$ahash = @{}
$afile | ForEach-Object {
$ahash[$_] = $true
}
$bfile = Get-Content (Read-Host "Enter 'B' file")
$bhash = @{}
$bfile | ForEach-Object {
$bhash[$_] = $true
}
$afile | Where-Object {
-not $bhash.ContainsKey($_)
} | Set-Content 'lines_ONLY_in_A.txt'
$bfile | Where-Object {
-not $ahash.ContainsKey($_)
} | Set-Content 'lines_ONLY_in_B.txt'
$afile | Where-Object {
$bhash.ContainsKey($_)
} | Set-Content 'lines_in _BOTH_A_and_B.txt'
这和你的另一个问题有关吗?这是一个独立的问题。您所指向的文件使用-match。如果对文件进行了排序,或者您保持了它们的排序,那么您可能需要执行一个喜欢二进制搜索策略的操作,我正在研究powershell或python解决方案。非常感谢。谢谢你们两位,@ansgar wiechers。我正在测试这些建议,并将向您汇报。谢谢。我需要一段时间才能回来报告,因为我正在用大文件测试这些选项;比较对象:1850ms;哈希表查找:250ms。谢谢,非常有用的信息。由于我的测量有限,哈希表似乎是最快的。仍在测试。@Ansgar Wiechers,感谢您的基准测试,假设“几千行”是3000行,我已经计算过,并在回答中解释了a在技术上应该在哪里以及如何在长期内获得超过标准搜索的优势。@iRon 250毫秒的标记来自哈希表方法。哈希表进行索引查找,而不是线性搜索,-contains运算符就是这样做的,这使得它非常慢,因此您永远不会达到收支平衡。相反,随着阵列大小的增加,差异将进一步增大。索引查找总是比二进制搜索快得多。 (log2 3000 + 1) / 2 = (11.55 + 1) / 2 = 6.27 56 * ((x + 1) / 2 * 3000) = 127482 * ((log2 x + 1) / 2 * 3000)
$afile = Get-Content (Read-Host "Enter 'A' file")
$ahash = @{}
$afile | ForEach-Object {
$ahash[$_] = $true
}
$bfile = Get-Content (Read-Host "Enter 'B' file")
$bhash = @{}
$bfile | ForEach-Object {
$bhash[$_] = $true
}
$afile | Where-Object {
-not $bhash.ContainsKey($_)
} | Set-Content 'lines_ONLY_in_A.txt'
$bfile | Where-Object {
-not $ahash.ContainsKey($_)
} | Set-Content 'lines_ONLY_in_B.txt'
$afile | Where-Object {
$bhash.ContainsKey($_)
} | Set-Content 'lines_in _BOTH_A_and_B.txt'