windows 查找前3个字符的所有唯一匹配项,计算匹配项的数量并将输出写入文件

qyuhtwio  于 2022-12-05  发布在  Windows
关注(0)|答案(3)|浏览(159)

我有一个超过2. 5亿行的文本文件。每一行都有一个3位数的区号,后面是一个逗号和一个7位数的数字。
输入文件示例:
201,2220000
201,5551212
310,5552481
376,1239876
443,0002222
572,8880099
...
我想生成一个输出文件,其中列出了每个唯一的区号和该区号的出现次数(仅查看每行的前3个字符)。
输出示例(区号、计数):
201、44556
202、34529
...
我在Windows 10环境中工作。
经过大量的研究,我能够在PowerShell中使用Switch函数与regex实现非常接近的东西。这个解决方案的问题是,我需要知道我正在寻找的区号(我不知道这个文件中列出的所有区号)。
我想修改解决方案,使它找到所有唯一的区号,然后运行代码。
以下是我尝试过的方法:
1.比方说,我想搜索以下四个区号:201,202,203,205
1.我的文本文件是datafile.txt

$count1 = 0
$count2 = 0
$count3 = 0
$count4 = 0
switch -File C:\datafile.txt -Exact -Regex { '201\S{8}' { ++$count1 } }
Write-Output "Area Code 201: $($count1)" | Format-Table | Out-File "C:\summary.txt" -append
switch -File C:\datafile.txt -Exact -Regex { '202\S{8}' { ++$count2 } }
Write-Output "Area Code 202: $($count2)" | Format-Table | Out-File "C:\summary.txt" -append
switch -File C:\datafile.txt -Exact -Regex { '203\S{8}' { ++$count3 } }
Write-Output "Area Code 203: $($count3)" | Format-Table | Out-File "C:\summary.txt" -append
switch -File C:\datafile.txt -Exact -Regex { '205\S{8}' { ++$count4 } }
Write-Output "Area Code 204: $($count4)" | Format-Table | Out-File "C:\summary.txt" -append

这段代码生成summary.txt文件,并将计数附加到区号后面。
1.我需要知道此数据文件中的所有区号。
1.我必须为每一个额外的区号添加3行代码。
如果能帮助改进此代码或使用替代解决方案,我将不胜感激(我在Stackoverflow上发现了一个使用grep https://stackoverflow.com/questions/61229157/using-regex-in-grep-for-windows-command-line的线程,但它有同样的限制-您需要知道您正在搜索的字符串。

vcudknz3

vcudknz31#

假设我的理解是正确的,这里不需要regex,只需要.SubString(0, 3)来获取每行的前3个字符,还有一个hashtable来确保代码的唯一性和效率。
确实,switch -File非常适合这个任务,应该用来读取文件。否则,为了简单和保持效率,你可以使用File.ReadLines

$map = @{ }
switch -File path\to\source\file.txt {
    Default {
        $map[$_.Substring(0, 3)] += 1
    }
}

$map.GetEnumerator() | ForEach-Object {
    [pscustomobject]@{
        Code  = $_.Key
        Count = $_.Value
    }
} | Export-Csv path\to\resultOfUniqueCodes.csv -NoTypeInformation
ttcibm8c

ttcibm8c2#

只是我对流媒体方法的两分钱--试图避免相对缓慢的一切,比如ForEach-ObjectpscustomobjectExport-Csv

# Create a scriptblock to be able to pipe output of foreach loop
& { 
    foreach( $line in [IO.File]::ReadLines( 'input.txt' ) ) { 
        $line.Substring( 0, 3 )
    }
} | Group-Object -NoElement | & {
    begin {
        'Code,Count'
    }
    process {
        '{0},{1}' -f $_.Name, $_.Count
    }
} | Set-Content output.csv

备注:

  • foreach( $line in [IO.File]::ReadLines( 'input.txt' ) )缓慢地处理输入文件,所以它作为一个整体读入内存。这是因为ReadLines返回一个foreach理解的迭代器(而不是集合)。正如其他人提到的,ReadLines被认为是逐行处理文本文件的最快方法之一,同时仍然提供易用性(例如与使用.NET流相比)。
  • Group-Object -NoElement只计算唯一输入元素的出现次数,可能使用内部哈希表,所以它应该和手动创建的哈希表一样快(虽然没有测量--这真的很有趣)。
  • Group-Object到脚本块的管道比ForEach-Object和脚本块的管道要快得多,请参见GitHub issue。虽然在您的情况下这并不重要,但瓶颈将是阅读和处理输入文件。
  • 由于输入数据的类型是已知的,我们可以避免Export-Csv的复杂性(如转义规则),并直接使用简单的字符串操作和Set-Content创建CSV。同样,在这里不会有什么不同,但对于其他输出量更大的情况,了解这一点可能仍然是件好事。
a64a0gku

a64a0gku3#

请尝试以下操作:

$input = @"
area,number
201,44556
202,34529
201,44556
202,34529
201,44556
202,34529
201,44556
202,34529
"@

$table = $input | ConvertFrom-Csv
$table | Format-Table

$groups = $table | Group-Object {$_.area}

$outputTable = [System.Collections.ArrayList]::new()
foreach($group in $groups)
{
$group | Format-Table

   $newRow = New-Object -TypeName psobject
   $newRow | Add-Member -NotePropertyName area -NotePropertyValue $group.Name

   $newRow | Add-Member -NotePropertyName count -NotePropertyValue $group.Count

   $outputTable.Add($newRow)  | Out-Null
}
$outputTable | Format-Table

相关问题