regex PowerShell正则表达式用于解释带有可选组的时间字符串12h34m56s

bejyjqdl  于 2023-04-07  发布在  Shell
关注(0)|答案(3)|浏览(110)

使用PowerShell和正则表达式,我试图解释数据文件中的时间字符串,例如字符串12h30m表示12小时30分钟,或15m表示15分钟,1m30s2h等。我想我可以使用正则表达式拆分这些字符串以分隔小时分钟和秒,并且只得到数字部分,所以省略hms,然后做一些计算。
然而,我设法让一个正则表达式工作,但只有当我还包括hms时,我的意思是当我在正则表达式中添加“积极前瞻”部分时,最终结果不是我所期望的。
下面是PowerShell脚本

$input = "12h34m56s"
#$input = "4h30m"
#$input = "1m15s"
#$input = "14400"

Write-Output "--(test 1)--------------------"
$input -match '(\d*h)?(\d*m)?(\d*s)?(\d*)?'
Write-Output $Matches

Write-Output "--(test 2)--------------------"
$input -match '(\d*(?=h))?(\d*(?=m))?(\d*(?=s))?(\d*)?'
Write-Output $Matches

输出如下:

--(test 1)--------------------
True

Name                           Value
----                           -----
4
3                              56s
2                              34m
1                              12h
0                              12h34m56s
--(test 2)--------------------
True
4
1                              12
0                              12

第一部分“test1”是我所期望的,但是对于第二部分“test2”,我期望的是这样的输出

--(test 2)--------------------
True

Name                           Value
----                           -----
4
3                              56
2                              34
1                              12
0                              12h34m56s

我测试了这个on regex101,看语法颜色似乎是正确的,但在顶部它说“27匹配”,我希望只有5个匹配(因为5行)。所以我怀疑它与分组有关。我试图在整个周围添加额外的括号,但没有帮助。任何帮助都将不胜感激。

u5rb5r59

u5rb5r591#

如果你想用正则表达式来做这件事,你可能会期待一个正则表达式的答案,但未来的读者应该意识到这应该使用TimeSpan.ParseExact来完成。
注意,调用此重载时需要强制转换[string[]]

$formats = [string[]]@(
    'hh\hmm\mss\s'
    'h\hmm\m'
    'm\mss\s'
)

$inputStrings = @(
    '12h34m56s'
    '4h30m'
    '1m15s'
    '14400'
)

$inputStrings | ForEach-Object {
    if($_ -notmatch '\D') {
        return [timespan]::FromSeconds($_)
    }

    [timespan]::ParseExact($_, $formats, [cultureinfo]::InvariantCulture)
}
7xllpg7q

7xllpg7q2#

正如mclayton评论的那样,您可以在许多格式上使用[timespan]::ParseExact()方法。
在你的例子中,唯一的问题是当字符串只包含数字时,在这种情况下,使用[timespan]::new()构造函数并将字符串的数值乘以10000000以将秒转换为Ticks:

$timeString = '1m15s'
if ($timeString -match '\D') {
    # try ParseExact using the following formats
    $formats =  'h\h','h\hm\ms\s','h\hm\mss\s','h\hmm\m','h\hmm\ms\s','h\hmm\mss\s',
                'hh\h','hh\hmm\m','hh\hmm\ms\s','hh\hmm\mss\s',
                'm\m','m\ms\s','m\mss\s','mm\m','mm\ms\s','mm\mss\s','s\s','ss\s'
    [timespan]::ParseExact($timeString, [string[]]$formats, $null)
}
else {
    # only digits, return a new timespan using the Ticks constructor
    [timespan]::new([long]$timeString * 10000000)
    # or use 
    # [timespan]::FromSeconds([double]$timeString)
}
bvpmtnay

bvpmtnay3#

使用**[timespan]::ParseExact(),如其他有用的答案所示,如果/一旦您有表示时间跨度的单独令牌,则绝对是首选。
然而,至少假设你可能(首先)必须从一个更大的文本*中提取这样的标记 *,在这种情况下,需要正则表达式 *-见下文。
你的正则表达式的
问题**是使用lookaheadAssert(例如,(?=h))会阻止你的正则表达式将12h34m56s等标记识别为 * 单一 * 匹配,因为lookaroundAssert不会 * 消耗 * 它们匹配的子字符串。
因此,只需直接匹配这些字符,并将子表达式包含在(?:…)中,即 * 非捕获 * 组,以避免不必要的捕获组;例如,使用(?:(\d+)h)?代替(\d*h)?
还要注意使用+而不是+,因为至少应该存在 * 一个 * 数字,而子表达式 * 作为整体 * 可能不存在((?:…)?
将所有这些放在一起,沿着使用 named 捕获组,这使得更容易识别哪个捕获组匹配捕获了哪些单元:

$str = @'
12h34m56s545294385
1h2m3s
4h30m
1m15s
14400
'@

[regex]::Matches($str, '(?:(?<hrs>\d+)h)?(?:(?<mins>\d+)m)?(?:(?<secs>\d+)s)?(?<num>\d+)?') |
  ForEach-Object { 
    if ($_.Value) { # Only consider nonempty matches.
      [pscustomobject] @{ 
        Match = $_.Value
        Groups = $_.Groups | Select-Object -Skip 1 | Select-Object Name, Value | Out-String
      } 
    }
  } | Format-Table -Wrap

输出:

Match              Groups
-----              ------
12h34m56s545294385
                   Name Value
                   ---- -----
                   hrs  12
                   mins 34
                   secs 56
                   num  545294385
                  
                  
1h2m3s            
                   Name Value
                   ---- -----
                   hrs  1
                   mins 2
                   secs 3
                   num
                  
                  
4h30m             
                   Name Value
                   ---- -----
                   hrs  4
                   mins 30
                   secs
                   num
                  
                  
1m15s             
                   Name Value
                   ---- -----
                   hrs
                   mins 1
                   secs 15
                   num
                  
                  
14400             
                   Name Value
                   ---- -----
                   hrs
                   mins
                   secs
                   num  14400

相关问题