PowerShell获取特定子字符串位置的总和 [英] powershell get the sum of a specific substring position
问题描述
如果满足以下条件,如何使用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-Content
和ForEach-Object
一起使用会增加另一个系数4。
这篇关于PowerShell获取特定子字符串位置的总和的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!