最后一场比赛结束的锚点

Anchor to End of Last Match

在处理 的过程中,我偶然发现了 Python 的重复正则表达式异常。

假设我得到一个 CSV 字符串,其中包含任意数量的带引号和不带引号的元素:

21, 2, '23.5R25 ETADT', 'description, with a comma'

我想用 '\t' 替换所有 ',' 外引号。所以我想要一个输出:

21\t2\t'23.5R25 ETADT'\t'description, with a comma'

由于字符串中自然会有多个匹配项,因此我将使用 g 正则表达式修饰符。我将使用的正则表达式将匹配引号外的字符或后跟 ',':

的引号字符串
('[^']*'|[^',]*),\s*

我将替换为:

\t

现在的问题是正则表达式是搜索而不是匹配所以它可以选择跳过字符直到它可以匹配。所以我得到的不是我想要的输出:

21\t2\t'23.5R25 ETADT'\t'description\twith a comma'

您可以在此处查看此行为的实例:https://regex101.com/r/sG9hT3/2

问。有没有办法锚定 g 修改后的正则表达式以在上一个匹配项之后的字符处开始匹配?


对于那些熟悉 Perl 强大的正则表达式的人来说,Perl 提供了 \G。这允许我们检索最后一个匹配位置的结尾。所以在 Perl 中我可以用正则表达式完成我所要求的:

\G('[^']*'|[^',]*),\s*

这将强制在最终引用的元素中出现不匹配。因为不是让正则表达式实现找到正则表达式与 \G 匹配的点,而是会强制它在 first 字符处开始匹配:

'description, with a comma'

您可以将以下正则表达式与 re.search 一起使用:

,?\s*([^',]*(?:'[^']*'[^',]*)*)

regex demo(我改成,?[ ]*([^',\n]*(?:'[^'\n]*'[^',\n]*)*)因为是多行演示)

在这里,正则表达式匹配(在单词的正则表达式含义中)...

  • ,? - 1 或 0 个逗号
  • \s* - 0 个或更多空格
  • ([^',]*(?:'[^']*'[^',]*)*) - 第 1 组存储捕获的文本,包含...
    • [^',]* - ,'
    • 以外的 0 个或多个字符
    • (?:'[^']*'[^',]*)* - 0 个或多个序列......
      • '[^']*' - 类似 'string' 的不包含撇号的子字符串
      • [^',]* - ,'.
      • 以外的 0 个或多个字符

如果您想使用 re.match 并将捕获的文本存储在捕获组中,这是不可能的,因为 Python 正则表达式引擎不会将所有捕获存储在堆栈中作为 .NET 正则表达式引擎使用 CaptureCollection.

此外,Python 正则表达式不支持 \G 运算符,因此您不能在此处成功匹配结束时锚定任何子模式。

作为一个alternative/workaround,你可以使用下面的Python代码来return个连续的匹配然后剩下的字符串:

import re

def successive_matches(pattern,text,pos=0):
  ptrn = re.compile(pattern)
  match = ptrn.match(text,pos)
  while match:
    yield match.group()
    if match.end() == pos:
      break
    pos = match.end()
    match = ptrn.match(text,pos)
  if pos < len(text) - 1:
    yield text[pos:]

for matched_text in successive_matches(r"('[^']*'|[^',]*),\s*","21, 2, '23.5R25 ETADT', 'description, with a comma'"):
    print matched_text

IDEONE demo,输出为

21, 
2, 
'23.5R25 ETADT', 
'description, with a comma'