如何将一系列以连字符分隔的数字分别填充为两位数?

How can I pad a series of hyphen-separated numbers each to two digits?

我是 PowerShell 编程的新手,因此需要有关我拥有的字符串集的帮助,如下所述:

"14-2-1-1"
"14-2-1-1-1"
"14-2-1-1-10"

如果 19 之间的每个数字,我想用零填充 - 之间的数字。所以结果应该是这样的:

"14-02-01-01"
"14-02-01-01-01"
"14-02-01-01-10"

我想出了以下代码,但想知道是否有 better/faster 解决方案。

$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

我倾向于使用 之类的东西,因为它很清晰,但这是另一种看待它的方式。下面的最后一节显示了一个解决方案,该解决方案可能与它自己的一些优点一样清晰。

输入中数字组的模式

您的输入字符串由一组或多组组成,其中每组...

  1. ...包含一位或多位数字,并且...
  2. ...前面是 - 或字符串的开头,并且...
  3. ...后跟 - 或字符串结尾。

需要插入前导 "0" 的组只包含一个数字;也就是说,它们包括...

  1. ...a - 或字符串的开头,后跟...
  2. ...单个数字,后跟...
  3. ...a - 或字符串结尾。

使用 -replace 运算符替换 single-digit 数字组

我们可以使用 regular expressions with the -replace operator 来定位该模式并将单个数字替换为 "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' }

...输出...

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)(?=-|$)' 表示...

替换模式

替换模式'0'意味着...

  • 文字 '0',后跟...
  • 第一次捕获的值((\d))

使用 [Regex]::Replace() 和替换 [String]

替换 single-digit 数字组

除了 -replace 运算符,您还可以调用 static Replace() method of the [Regex] class...

'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') }

...结果是一样的。

使用 [Regex]::Replace()[MatchEvaluator]

替换数字组

正则表达式和 imperative solutions is to call an overload of the Replace() method that takes a [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) }

这会产生与上面相同的结果。

A [MatchEvaluator] is a delegate that takes a Match to be replaced ($match) and returns the [String] with which to replace it (the matched text left-padded to two digits). Also note that, whereas above we are capturing only standalone digits (\d), here we capture all groups of one or more digits (\d+) and leave it to PadLeft() 以确定是否需要前导 "0"

我认为这是一个比单独使用正则表达式更有吸引力的解决方案,因为它是最好的解决方案和 imperative 世界:

  • 它使用简单的正则表达式模式来定位输入字符串中的数字组
  • 它使用一个简单的[ScriptBlock]来转换输入字符串中的数字组
  • 通过不拆分输入字符串,它不会创建尽可能多的中间字符串和数组 garbage
    • 这种潜在的性能改进是否完全被使用正则表达式所掩盖,我不能说没有基准测试