我对PowerShell编程还很陌生,所以需要帮助处理下面描述的一组字符串:
"14-2-1-1"
"14-2-1-1-1"
"14-2-1-1-10"
如果在1
和9
之间,我想在-
之间的每个数字上加零。所以结果应该是:
"14-02-01-01"
"14-02-01-01-01"
"14-02-01-01-10"
我想出了以下代码,但想知道是否有更好/更快的解决方案。
$Filenum = "14-2-1-1"
$hicount = ($Filenum.ToCharArray() | Where-Object{$_ -eq '-'} | Measure-Object).Count
$FileNPad = ''
For ($i=0; $i -le $hicount; $i++) {
$Filesec= "{$i}" -f $Filenum.split('-')
If ([int]$Filesec -le 9)
{
$FileNPad = "$FileNPad-"+"0"+"$Filesec"
}
Else
{
$FileNPad="$FileNPad-$Filesec"
}
}
$FileNPad = $FileNPad.Trim("-"," ")
您可以简单地在-
、padleft上拆分,然后与-
重新连接在一起,而不是手动跟踪元素数量并检查每个值
"14-2-1-1","14-2-1-1-1","14-2-1-1-10" | ForEach-Object {
($_ -split '-').PadLeft(2,'0') -join '-'
}
哪个输出
14-02-01-01
14-02-01-01-01
14-02-01-01-10
我倾向于采用Doug Maurer的答案,因为它很清晰,但这里有另一种方法。下面的最后一节展示了一个解决方案,该解决方案可能同样清晰,但也有其自身的一些优势。
输入中的数字组模式
您的输入字符串由一个或多个组组成,其中每个组。。。
- 。。。包含一个或多个数字,并且
- 。。。前面是
-
或字符串的开头,并且 - 。。。后面跟一个CCD_ 7或字符串的末尾
需要插入前导"0"
的组正好包含一位数字;也就是说,它们由。。。
- 。。。
-
或字符串的开头,后跟 - 。。。一个位数,后面跟
- 。。。CCD_ 10或字符串的末尾
使用-replace
运算符替换个位数组
我们可以使用带有-replace
运算符的正则表达式来定位该模式,并将单个数字替换为后面跟有相同数字的"0"
。。。
'0-0-0-0', '00-00-00-00', '1-2-3-4', '01-02-03-04', '10-20-30-40', '11-22-33-44' |
ForEach-Object -Process { $_ -replace '(?<=-|^)(d)(?=-|$)', '0$1' }
其输出。。。
00-00-00-00
00-00-00-00
01-02-03-04
01-02-03-04
10-20-30-40
11-22-33-44
正如文档所描述的,-replace
运算符是这样使用的。。。
<input> -replace <regular-expression>, <substitute>
匹配模式
匹配模式'(?<=-|^)(d)(?=-|$)'
表示。。。
(?<=-|^)
:-
或字符串开头的零宽度正后备断言- 换句话说,匹配但不捕获
-
或字符串的开头
- 换句话说,匹配但不捕获
(d)
:单个数字,捕获并可用替换$1
(?=-|$)
:-
或字符串末尾的零宽度正向前瞻断言- 换句话说,匹配但不捕获
-
或字符串结尾
- 换句话说,匹配但不捕获
替换模式
替换图案'0$1'
表示。。。
- 文字文本
'0'
,后面跟 - 第一次捕获的值(
(d)
(
使用[Regex]::Replace()
和替换[String]
替换个位数组
您也可以调用[Regex]
类的static
Replace()
方法来代替-replace
运算符。。。
'0-0-0-0', '00-00-00-00', '1-2-3-4', '01-02-03-04', '10-20-30-40', '11-22-33-44' |
ForEach-Object -Process { [Regex]::Replace($_, '(?<=-|^)(d)(?=-|$)', '0$1') }
并且结果是相同的。
使用[Regex]::Replace()
和[MatchEvaluator]
替换数字组
正则表达式和命令式解决方案的混合是调用Replace()
方法的重载,该方法使用[MatchEvaluator]
而不是替换[String]
。。。
# This [ScriptBlock] will be passed to a [System.Text.RegularExpressions.MatchEvaluator] parameter
$matchEvaluator = {
# The [System.Text.RegularExpressions.Match] parameter
param($match)
# The replacement [String]
return $match.Value.PadLeft(2, '0')
}
'0-0-0-0', '00-00-00-00', '1-2-3-4', '01-02-03-04', '10-20-30-40', '11-22-33-44' |
ForEach-Object -Process { [Regex]::Replace($_, '(d+)', $matchEvaluator) }
这会产生与上面相同的结果。
[MatchEvaluator]
是一个委托,它接受要替换的Match
($match
(,并返回要替换它的[String]
(左填充到两位的匹配文本(。还要注意,尽管上面我们只捕获独立的数字(d)
,但这里我们捕获一个或多个数字(d+)
的所有组,并将其留给PadLeft()
来确定是否需要前导"0"
。
我认为这是一个比正则表达式更令人信服的解决方案,因为它是其中最好的一个,也是命令式世界:
- 它使用一个简单的正则表达式模式来定位输入字符串中的数字组
- 它使用一个简单的
[ScriptBlock]
来转换输入字符串中的数字组 - 通过不将输入字符串拆分,它不会创建那么多的中间字符串和数组垃圾
- 如果没有基准测试,我不能说使用正则表达式是否掩盖了这种潜在的性能改进