PowerShell获取特定子字符串位置的总和 [英] powershell get the sum of a specific substring position

查看:13
本文介绍了PowerShell获取特定子字符串位置的总和的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果满足以下条件,如何使用PowerShell从子字符串中获取文件总和并将总和放在特定位置(不同行):

获取以字符D开头的行的第3位到第13位数字的总和。将总和放在以S开头的行的第10位到第14位

例如,如果我有这个文件:

F123trial   text
DA00000038.95==xxx11
DA00000018.95==yyy11
DA00000018.95==zzzyy
S        xxxxx

我想得到38.95、18.95和18.95的总和,然后将总和放在以S开头的线下的xxxxx位置

推荐答案

PowerShell的switch statement具有强大但鲜为人知的功能,允许您迭代文件的行(-file)并通过正则表达式匹配行(-regex)

switch -file不仅方便,而且比在管道中使用cmdlet快得多(见下一节)。

[double] $sum = 0

switch -regex -file file.txt {

  # Note: The string to the left of each script block below ({ ... }), 
  #       e.g., '^D', is the regex to match each line against.
  #       Inside the script blocks, $_ refers to the input line at hand.

  # Extract number, add to sum, output the line.
  '^D' { $sum += $_.Substring(2, 11); $_; continue }

  # Summary line: place sum at character position 10, with 0-padding
  # Note: `-replace ',', '.'` is only needed if your culture uses "," as the
  #       decimal mark.
  '^S' { $_.Substring(0, 9) + '{0:000000000000000.00}' -f $sum -replace ',', '.'; continue }
  
  # All other lines: pass them through.
  default { $_ }

}

注意:

  • 脚本中的continue阻止进一步匹配手边行的短路;相比之下,如果使用break将不再处理其他行
  • 根据后面的评论,我假设您需要在S行的10字符位置添加一个18个字符的0左填充数字。

对于您的样例文件,上面的结果如下:

F123trial   text
DA00000038.95==xxx11
DA00000018.95==yyy11
DA00000018.95==zzzyy
S        000000000000076.85

可选阅读:switch -file ...Get-Content ... | ForEach-Object ...的性能比较

运行以下测试脚本:

& {
  # Create a sample file with 100K lines.
  1..1e5 > ($tmpFile = [IO.Path]::GetTempFileName())
  (Measure-Command { switch -file ($tmpFile) { default { $_ } } }).TotalSeconds, 
  (Measure-Command { get-content $tmpFile | % { $_ }  }).TotalSeconds
  Remove-Item $tmpFile
}

在我的机器上生成以下计时,例如(绝对数并不重要,但它们的比率应该会让您有所了解):

0.0578924   # switch -file
6.0417638   # Get-Content | ForEach-Object

基于管道的解决方案约为100(!)比switch -file解决方案慢几倍。


深入挖掘:

Frode F.指出,Get-Content处理大文件的速度很慢--尽管它的便利性使其成为一个流行的选择--并提到直接使用.NET框架作为替代:

  • 使用[System.IO.File]::ReadAllLines();但是,如果它将整个文件读入内存,则这只是较小文件的一个选项。

  • 在循环中使用[System.IO.StreamReader]ReadLine()方法。

但是,使用管道本身,而不考虑使用的特定cmdlet,会带来开销。当性能很重要的时候--但只有在那时--你应该避免它。

下面是一个更新的测试,其中包括使用.NET框架方法的命令,使用和不使用管道(使用集合运算符.ForEach()需要PSv4+):

& {
  # Create a sample file with 100K lines.
  1..1e5 > ($tmpFile = [IO.Path]::GetTempFileName())
  
  (Measure-Command { switch -file ($tmpFile) { default { $_ } } }).TotalSeconds
  (Measure-Command { 
    $sr = [IO.StreamReader] (Convert-Path $tmpFile)
    while(-not $sr.EndOfStream) { $sr.ReadLine() }
    $sr.Close() 
  }).TotalSeconds
  (Measure-Command { [IO.File]::ReadAllLines((Convert-Path $tmpFile)).ForEach({ $_ }) }).TotalSeconds
  (Measure-Command { [IO.File]::ReadAllLines((Convert-Path $tmpFile)) | % { $_ } }).TotalSeconds
  (Measure-Command { Get-Content $tmpFile | % { $_ }  }).TotalSeconds
  
  Remove-Item $tmpFile
}

样本结果,从快到慢:

0.0571143  # switch -file
0.2035162  # [System.IO.StreamReader] in a loop
0.6756535  # [System.IO.File]::ReadAllText() with .ForEach() collection operator
1.5088355  # (pipeline) [System.IO.File]::ReadAllText() with ForEach-Object
5.9815751  # (pipeline) Get-Content with ForEach-Object

switch -file是最快的,大约是3倍,其次是.NET+循环解决方案;使用.ForEach()又增加了3倍。 简单地引入管道(ForEach-Object而不是.ForEach())会增加另一个系数2;最后,将管道与Get-ContentForEach-Object一起使用会增加另一个系数4。

这篇关于PowerShell获取特定子字符串位置的总和的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆