【问题标题】:Use of capture groups within lookarounds在环视中使用捕获组
【发布时间】:2020-03-10 19:05:43
【问题描述】:

假设我们得到以下字符串:

a, b, c, d
e, f, g, h
i, j, k, l

我希望使用 PCRE 正则表达式将其转换为以下字符串:

ab, ac, ad
ef, eg, eh
ij, ik, il

更一般地,这些字母中的每一个都可以看作是一串单词字符的占位符,每行可以有任意数量的字符,也可以有任意数量的行。

如果做不到,能否产生如下字符串?

a, ab, ac, ad
e, ef, eg, eh
i, ij, ik, il

请在 regex101.com 上使用“SUBSTITUTION”工具(可以包括反向引用,例如 $1)演示您的正则表达式。我会特别感谢 PCRE 引擎如何单步执行字符串的解释。

如果这不能用 PCRE 正则表达式完成,我想解释一下为什么它不能完成。

我问这个问题是为了加深我对环视内捕获组如何工作的理解。

【问题讨论】:

  • 我认为不需要环顾四周。只是普通的捕获组。
  • 只写一个正则表达式匹配两个用逗号分隔的单词,可选地后跟另一个逗号。将单词和可选逗号放在捕获组中,并将它们复制到替换中。
  • “请演示您的正则表达式...”听起来像是您从练习中引用的,而不是您自己的问题。
  • 我没有注意到每个替换都包含该行的第一个单词,它们不是成对的。我不认为你可以用一个正则表达式来做到这一点,它们不能循环。
  • @Barman,我认为那是因为您在这个论坛上习惯于模糊、模棱两可的问题。我提出这个问题是为了了解我不理解的正则表达式的各个方面,并试图使问题完整且明确。请按面值接受。

标签: regex pcre


【解决方案1】:

这只能通过支持可变宽度后向模式的正则表达式引擎来完成,而 PCRE 不支持。需要一个可变宽度的lookbehind来引用每行开头的每个后续单词的单词。

如果您的正则表达式引擎支持可变宽度后向模式,您可以搜索:

(?<=(\w+),.*)(\w+)|^\w+,\s*

并将匹配项替换为:

$1$2

演示:https://regex101.com/r/XZhZyW/5/

【讨论】:

  • 您应该考虑发布一个有效的演示。我真的很想赞成这个答案,但不能以目前的状态。
  • 确实如此。然后修复。
  • 我发现了问题,我使用的是 Firefox。一定要添加关于浏览器支持的脚注。
  • 这对我的理解非常有帮助。基本上,lookbehind 捕获第一个单词字符串,然后在某种意义上将引擎维护的指针移动到第一个匹配项。如果这是正确的,你能详细解释一下指针方面吗?我以前认为这样的指针不会在环视中移动。我尝试过的一件事是用(\w+){0} 开始正则表达式,然后正则表达式会尝试匹配字符串开头的内容吗?
  • 您在倒数第二条评论中的解释既清晰又值得赞赏。我认为这可能会设置 $1 而不会匹配。顺便说一句,我注意到您可以稍微简化一下,就像您在编辑时所做的那样。我们可能已经尽我们所能。我不会忘记选择答案。
【解决方案2】:

我想提一下在遇到某种情况时可能采取的行动方案,例如这里,需要可变长度的正向回溯,但所使用的正则表达式引擎不支持该操作,但确实支持可变长度的正向回溯,例如 PCRE (PHP)。

我承认写这个答案主要是为了提高我自己对正则表达式引擎如何运作的理解。

基本思路

  • 反转字符串
  • 使用带有相应正向预测的正则表达式来替换匹配项
  • 反转结果字符串

示例

假设我们希望转换以下字符串:

a, bb, c, d
ee, f, g, h
i, j, kk, l

到字符串:

abb, ac, ad
eef, eeg, eeh
ij, ikk, il

我们先把原来的字符串倒过来:

d ,c ,bb ,a
h ,g ,f ,ee
l ,kk ,j ,i

然后匹配正则表达式:

(\w+)(?=.*,(\w+)$)|\s+,\w+$ 

并将每个匹配项替换为$1+$2,以获得:

da ,ca ,bba
hee ,gee ,fee
li ,kki ,ji

最后,反转这些字符串:

abb, ac, ad
eef, eeg, eeh    
ij, ikk, il

PCRE demo

正则表达式执行以下操作:

(\w+)    # match 1+ word chars in cap grp 1 
(?=      # begin a positive lookahead
  .*,    # match 0+ chars (greedily), then ','
  (\w+)  # match 1+ word chars in cap grp 1
  $
)
|
\s+,\w+
$ 

我将用小表情符号 () 表示字符串 "a, bb, c, d" 中的空格,以使它们更加清晰,并因此显示字符串:

 a , ☺ b b , ☺ c , ☺ d
^

空格现在表示相邻字符之间的区域。 ^ 是正则表达式引擎指针的初始位置。

(\w+) 与字符串开头的"a" 匹配之后(由下面的m 表示),"a" 被保存到捕获组 1。正向先行在该匹配之后立即开始:

 a , ☺ b b , ☺ c , ☺ d
 m^

正向前瞻,(?=.*,(\w+)$) 保存 "d" 以捕获第 2 组。1 由于匹配成功,第一个匹配 "a" 被替换为 $1+$2 #=&gt; "ad" 并且指针移动回到执行前瞻之前的位置:

 a , ☺ b b , ☺ c , ☺ d
  ^

现在尝试将(\w+) 与以第一个逗号开头的字符串部分匹配。这失败了,正则表达式的 or 部分 \s+,\w+$ 也是如此。然后指针前进一个字符:

 a , ☺ b b , ☺ c , ☺ d
    ^

同样失败,指针再次前移一。

 a , ☺ b b , ☺ c , ☺ d
      ^

(\w+) 现在匹配"bb",它被保存到捕获组 1,此时:

 a , ☺ b b , ☺ c , ☺ d
       m m^

和以前一样,正向前瞻保存"d" 以捕获第 2 组和匹配,"bb" 被替换为 $1+$2 #=&gt; "bbd"

在另外两次匹配失败后,我们处于:

 a , ☺ b b , ☺ c , ☺ d
              ^

出于与以前相同的原因,"c" 被匹配并替换为 $1+$2 =&gt; "cd",我们现在在这里:

 a , ☺ b b , ☺ c , ☺ d
                ^

后面没有要匹配的单词字符串,但是字符串的结尾 ", d" 现在匹配正则表达式的 or 部分 \s+,\w+$。然后将该匹配替换为$1+$2。然而这一次,两个捕获组是空的,所以匹配被替换为一个空字符串。

1 需要逗号。没有它,.* 贪婪会吞噬最后一个单词字符之前的所有内容。例如,如果字符串以 ", cd" 结尾,则捕获组 2 将仅包含 "d"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-22
    • 2018-02-07
    相关资源
    最近更新 更多