两个文件:仅保留前n个字符相同的行

时间:2017-11-05 08:30:52

标签: powershell

CWD中有2个文本文件,a.txtb.txt。从a.txt开始,我想删除b.txt中前5个字符不存在的所有行,作为任何行的前5个字符。 (或者,另外说明,只保留a.txt中的那些行,其前5个字符 出现在b.txt中,作为任何行的前5个字符。)第5个字符后面的内容到最后一行是无关紧要的。

例如:a.txt

abcde000dsdsddsdsdsdsdsd
0123456xxx
kkk
xyzxyzxyzfeeeee
kkkkkkkkkkk

b.txt

012345aabbcc
kkkkkkkhhkkvv
nnnnnnn5777nnnn77567

预期结果(a.txt中的行b.txt中包含1-5个字符的行:

0123456xxx
kkkkkkkkkkk

当我运行代码时,它会给我一个空的results.txt,但没有错误消息。我缺少什么?

$pattern = "^[5]"
$set1 = Get-Content -Path a.txt
$results = New-Object -TypeName System.Text.StringBuilder
Get-Content -Path b.txt | foreach {
    if ($_ -match $pattern) {
        [void]$results.AppendLine($_)
    }
}
$results.ToString() | Out-File -FilePath .\results.txt -Encoding ascii

3 个答案:

答案 0 :(得分:1)

您的代码不起作用,因为您的模式与任何内容都不匹配。正则表达式^[5]表示“字符串开头的字符'5'(方括号定义character class),而不是”字符串开头的5个字符“。后者将是^.{5}。此外,您永远不会将a.txt的内容与b.txt的内容相匹配。

有几种方法可以做你想做的事:

  • b.txt.的每一行中的前5个字符提取到数组,并将a.txt的行与该数组进行比较。 Esperento57's answer有点使用这种方法,但需要PowerShell v3或更新版本。适用于所有PowerShell版本的变体可能如下所示:

    $pattern = '^(.{5}).*'
    
    $ref = (Get-Content 'b.txt') -match $pattern -replace $pattern, '$1' |
           Get-Unique
    
    Get-Content 'a.txt' | Where-Object {
        $ref -contains ($_ -replace $pattern, '$1')
    } | Set-Content 'results.txt'
    
  • 由于数组中的查找速度相对较慢且无法很好地扩展(随着数组中元素数量的增加,它们会显着变慢),您也可以将参考值放在hashtable中,这样就可以了做索引查找(明显更快):

    $pattern = '^(.{5}).*'
    
    $ref = @{}
    (Get-Content 'b.txt') -match $pattern -replace $pattern, '$1' |
        ForEach-Object { $ref[$_] = $true }
    
    Get-Content 'a.txt' | Where-Object {
        $ref.ContainsKey(($_ -replace $pattern, '$1'))
    } | Set-Content 'results.txt'
    
  • 另一种方法是从b.txt中提取的子字符串中构建第二个regular expression,并将a.txt的内容与该表达式进行比较:

    $pattern = '^(.{5}).*'
    
    $list = (Get-Content 'b.txt') -match $pattern -replace $pattern, '$1' |
            Get-Unique |
            ForEach-Object { [regex]::Escape($_) }
    $ref  = '^({0})' -f ($list -join '|')
    
    (Get-Content 'a.txt') -match $ref | Set-Content 'results.txt'
    

请注意,这些方法中的每一种都会忽略短于5个字符的行。

答案 1 :(得分:0)

尝试这样的事情:

{{1}}

答案 2 :(得分:0)

如果需要考虑性能,请考虑使用散列表作为索引:

$Pattern = '^(.{5}).*'

$a = @{}; $b = @{}
Get-Content -Path a.txt | Where {$_ -Match $Pattern} | ForEach {$a[$Matches[1]] = @($a[$Matches[1]] + $_)}
Get-Content -Path b.txt | Where {$_ -Match $Pattern} | ForEach {$b[$Matches[1]] = @($b[$Matches[1]] + $_)}

$a.Keys | Where {$b.Keys -Contains $_} | ForEach {$a.$_} | Set-Content results.txt