【问题标题】:Why sed doesn't print an optional group?为什么 sed 不打印可选组?
【发布时间】:2013-05-19 02:53:32
【问题描述】:

我有两个字符串,比如 foo_barfoo_abc_bar。我想匹配他们两个,如果第一个匹配,我想用 = 符号强调它。所以,我的猜测是:

echo 'foo_abc_bar' | sed -r 's/(foo).*(abc)?.*(bar)/\1=\2=\3/g'
> foo==bar

echo 'foo_abc_bar' | sed -r 's/(foo).*((abc)?).*(bar)/\1=\2=\3/g'
> foo==

但正如上面的输出所示,它们都不起作用。

我如何指定一个可选组,如果字符串包含它则匹配,否则就跳过?

【问题讨论】:

  • 为什么在表达式中使用.*,下划线可以替换成任意字符串吗?

标签: regex string bash sed regex-group


【解决方案1】:

解决办法:

echo 'foo_abc_bar' | sed -r 's/(foo)_((abc)_)?(bar)/\1=\3=\4/g'

为什么您之前的尝试无效:

.* 是贪心的,所以对于正则表达式 (foo).*(abc)?.*(bar) 尝试匹配 'foo_abc_bar'(foo) 将匹配 'foo',然后 .* 最初将匹配字符串的其余部分 ('_abc_bar' )。正则表达式将继续,直到它到达所需的(bar) 组,这将失败,此时正则表达式将通过放弃与.* 匹配的字符来回溯。这将发生直到第一个.* 只匹配'_abc_',此时最后一个组可以匹配'bar'。因此,您的字符串中的 'abc' 不是在捕获组中匹配,而是在非捕获 .* 中匹配。

我的解决方案说明:

首先也是最重要的是将.* 替换为_,如果您知道分隔符是什么,则无需匹配任意字符串。接下来我们需要做的是弄清楚字符串的哪一部分是可选的。如果字符串'foo_abc_bar''foo_bar' 都有效,那么中间的'abc_' 是可选的。我们可以使用(abc_)? 将其放入可选组中。最后一步是确保我们在捕获组中仍然有字符串'abc',我们可以通过将该部分包装在一个额外的组中来做到这一点,所以我们最终得到((abc)_)?。然后我们需要调整替换,因为有一个额外的组,所以我们使用\1=\3=\4 而不是\1=\2=\3\2 将是字符串'abc_'(如果匹配)。请注意,在大多数正则表达式实现中,您也可以使用非捕获组并继续使用 \1=\2=\3,但 sed 不支持非捕获组。

另一种选择:

我认为上面的正则表达式是你最好的选择,因为它是最明确的(它只会匹配你感兴趣的确切字符串)。但是,您也可以通过使用惰性重复(匹配尽可能少的字符)而不是贪婪重复(匹配尽可能多的字符)来避免上述问题。您可以通过将.* 更改为.*? 来做到这一点,因此您的表达式将如下所示:

echo 'foo_abc_bar' | sed -r 's/(foo).*?(abc).*?(bar)/\1=\2=\3/g'

【讨论】:

    【解决方案2】:

    也许你可以简单地使用:

    echo 'foo_abc_bar' | sed -r 's/(foo|bar|abc)_?/\1=/g'
    echo 'foo_bar' | sed -r 's/(foo|bar|abc)_?/\1=/g'
    
    > foo=abc=bar=
    > foo=bar=
    

    这避免了foo==barfoo_bar 相同,我发现通过将= 有时在比赛之前,有时在比赛之后来表示强调有点奇怪。

    【讨论】:

      猜你喜欢
      • 2022-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-25
      • 1970-01-01
      • 2020-11-09
      • 1970-01-01
      • 2021-11-04
      相关资源
      最近更新 更多