【问题标题】:Is it possible to get match position using `switch -regex`是否可以使用`switch -regex`获得匹配位置
【发布时间】:2021-09-26 04:07:41
【问题描述】:

Select-String -Pattern 返回每​​个匹配的位置。例如:

$s = 'abcxyzabc' | Select-String -Pattern 'abc(?<x>.*)abc'
$s.Matches[0].Groups | Select-Object Name, Value, Index, Length

给予:

Name Value     Index Length
---- -----     ----- ------
0    abcxyzabc     0      9
x    xyz           3      3

其中IndexLength 指定每个匹配的位置。

但是,在使用switch -regex 时,我找不到如何获取每个匹配项的位置。例如:

switch -regex ('abcxyzabc') {
    'abc(?<x>.*)abc' {
        $matches
    }
}

给予:

Name                           Value
----                           -----
x                              xyz
0                              abcxyzabc

我找不到像 IndexLength 这样的东西来获取 $matches 中的匹配位置。

我还检查了Select-String -Pattern返回的Matches的类型是System.Text.RegularExpressions.Match[],而switch -regex$matches的类型是System.Collections.Hashtable

我错过了什么还是switch -regex 不应该提供每场比赛的位置?

【问题讨论】:

  • 尽管有 Powershell。在计算机编程语言中,switch 语句是一种选择控制机制,用于允许变量或表达式的值通过搜索和映射来改变程序执行的控制流。因此,布尔值,而不是字符串。就像一个简单的 if/then。 Switch 语句有两种主要变体:结构化和非结构化 switch。
  • 正如mklement0 所指出的,完全跳过switch 语句。 ([regex]::Match('abcxyzabc', 'abc(?&lt;x&gt;.*)abc')).Groups | Select-Object Name, Value, Index, Length,好吧,除非你只是渴望使用它,否则这些都是一样的。 switch -regex (([regex]::Match('abcxyzabc', 'abc(?&lt;x&gt;.*)abc')).Groups) { $PSItem {$PSItem | Select-Object Name, Value, Index, Length}}switch (([regex]::Match('abcxyzabc', 'abc(?&lt;x&gt;.*)abc')).Groups) {$PSItem {$PSItem | Select-Object Name, Value, Index, Length}}.

标签: regex powershell switch-statement


【解决方案1】:

继续我的评论。

https://adamtheautomator.com/powershell-switch/#Using_the_-RegEx_Parameter

参考下面的代码,第一行将导入内容 RegExp.txt 并将其存储在 $RegExp 变量中。然后, Powershell switch 语句使用 email.txt 文件作为输入 由 -file 参数指示的测试值。

$RegExp = Get-Content .\RegExp.txt
switch -regex -file .\email.txt {
    $RegExp {"[$_] is an email address"}
    Default {"[$_] is NOT an email address"}
}

一旦上面的代码在 PowerShell 中运行,只有测试值 匹配存储在 $RegExp 中的正则表达式将是 验证。

https://riptutorial.com/powershell/example/3791/switch-statement-with-regex-parameter

-Regex 参数允许 switch 语句执行常规 表达式匹配条件。

switch -Regex ('Condition')
{ 
  'Con\D+ion'    {'One or more non-digits'}
  'Conditio*$'   {'Zero or more "o"'} 
  'C.ndition'    {'Any single char.'}  
  '^C\w+ition$'  {'Anchors and one or more word chars.'} 
  'Test'         {'No match'} 
}

更新

相对于@daniel 替代有用的方法,与您的努力相结合;可以重构为这个。

switch -regex ('abcxyzabc') 
{
    {($script:myMatches = [regex]::Matches($PSItem, 'abc(?<x>.*)abc'))} 
    {
        $myMatches.Groups | 
        Select-Object Name, Value, Index, Length
    }
} 

# Results
<#
Name Value     Index Length
---- -----     ----- ------
0    abcxyzabc     0      9
x    xyz           3      3
#>

【讨论】:

    【解决方案2】:

    你可以使用这个丑陋的替代品,它直接使用 Regex 类,会给你索引和长度

    $values = @(
        'abcxyzabc'
        'cat in the hat'
        'the dogg pound'
    )
    
    switch ($values) {
        {($script:myMatches = [regex]::Matches($_, 'abc(?<x>.*)abc'))} {
            $myMatches.Groups[1]
        }
        {($script:myMatches = [regex]::Matches($_, 'cat(?<x>.*)hat'))} {
            $myMatches.Groups[1]
        }
    }
    

    结果

    Success  : True
    Name     : x
    Captures : {x}
    Index    : 3
    Length   : 3
    Value    : xyz
    
    Success  : True
    Name     : x
    Captures : {x}
    Index    : 3
    Length   : 8
    Value    :  in the
    

    【讨论】:

    • 我不认为这很丑,但超级hacky哈哈,你仍然可以使用switch -regex (...)让它看起来更干净
    • 伙计,你们都这么聪明(:
    • 有效,但有些麻烦。不幸的是,分支 condition 脚本块(意外地)在 child 范围内运行,这使解决方案变得复杂。
    • @SantiagoSquarzon,虽然在视觉上肯定更容易,但由于匹配正则表达式两次而效率低下(尽管您也只需使用 脚本块 作为条件,在实践中可能会更糟(尚未尝试测量)。
    • @mklement0 同意,我会亲自使用foreach loop,就像您在回答问题时所做的那样,但由于 OP 询问switch -Regex,这是我在看到丹尼尔的回答后的第一个想法。
    【解决方案3】:

    我错过了什么还是switch -regex不应该提供每场比赛的位置?

    确实,switch 语句的 -Regex 开关和(有效的)底层
    -match operator 都旨在提供除 text 被捕获的信息以外的信息
    ,通过automatic $Matches variable - 请参阅this answer 了解更多信息。

    获取匹配-位置信息(起始索引、长度)需要以下条件之一:

    将后者应用于您的示例:

    PS> [regex]::Match('abcxyzabc', 'abc(?<x>.*)abc')
    
    Groups   : {0, x}
    Success  : True
    Name     : 0
    Captures : {0}
    Index    : 0
    Length   : 9
    Value    : abcxyzabc
    

    Daniel's helpful answer 向您展示了一种通过switch 提供此功能的方式——有点麻烦。

    更有效的方法是将foreach statementif 语句结合起来:

    foreach ($str in 'abcxyzabc', '...') {
      if     (($match = [regex]::Match($str, 'abc(?<x>.*)abc')).Success) { <# ... #> }
      elseif (($match = [regex]::Match($str, 'cde(?<x>.*)cde')).Success) { <# ... #> }
      # ...
    }
    

    【讨论】:

    • if 中使用赋值看起来是迄今为止我见过的最佳选择。 if ($s = 'abcxyzabc' | Select-String -Pattern 'abc(?&lt;x&gt;.*)abc') 看起来比 [regex]::Match 更简单,因为它的括号更少。只有一个问题:我已经看到了关于在其他编程语言中使用if 条件中的赋值是否好的辩论,尽管它使代码更清晰。我没有在 PowerShell 语言中找到任何关于它的辩论,但我想这是我必须做出的权衡。
    • 在我看来,Select-String -Pattern[regex]::Match 返回匹配位置信息,而 switch -regex-match 运算符不返回匹配位置信息对我来说也很奇怪,因为行为不一致。
    • @Haoshu,至于不一致:PowerShell通常旨在提供更高级别的简化体验,这通常意味着简单性优于对底层.NET功能的完全访问(仍然可用对于那些了解底层 API 的人)。也就是说,您是正确的,手头上的 Select-String cmdlet 和另一个 switch 语句、-match 运算符之间存在不对称性。
    • @Haoshu,至于'abcxyzabc' | Select-String -Pattern 'abc(?&lt;x&gt;.*)abc' vs. [regex]::Match($str, 'abc(?&lt;x&gt;.*)abc'):是的,前者更像PowerShell,但由于使用了管道 和一个 cmdlet - 虽然这在实践中可能无关紧要。
    • @Haoshu,关于避免使用赋值作为表达式,比如if条件句:我知道=(赋值)之间存在混淆的可能性和-eq(比较)对于新手,但对于那些熟悉该语言的人来说,将赋值用作表达式的能力是一个强大的概念,它允许更简洁、更有表现力的代码。
    猜你喜欢
    • 1970-01-01
    • 2020-10-04
    • 2012-05-27
    • 1970-01-01
    • 2010-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多