【问题标题】:Anchor to End of Last Match锚定到最后一场比赛的结束
【发布时间】:2016-01-29 13:59:11
【问题描述】:

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

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

21, 2, '23.5R25 ETADT', '描述,带逗号'

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

21\t2\t'23.5R25 ETADT'\t'描述,带逗号'

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

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

我将替换为:

\1\t

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

21\t2\t'23.5R25 ETADT'\t'描述\t带逗号'

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

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


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

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

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

'描述,用逗号'

【问题讨论】:

  • 看看,?\s*('[^']*'|[^',]*)。或者更类似于演示的,?[ ]*('[^'\n]*'|[^',\n]*)
  • @stribizhev 是的,这两种方法都是可行的。如果这就是所有可以做的,我很好。但我想回答的是我们是否可以锚定此搜索或强制它匹配,而不仅仅是搜索。
  • 好吧,我想我明白了你的意思,如果你有任何疑问,请在我的回答下方留言。

标签: python regex search match repeat


【解决方案1】:

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

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

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

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

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

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

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

作为替代/解决方法,您可以使用以下 Python 代码返回连续匹配项,然后返回字符串的其余部分

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'

【讨论】:

  • 我的意思是即使 C++ 也可以做到这一点。您只需要找到匹配的长度,然后从该点开始重新运行正则表达式。因此,即使在 Python 中也肯定是可能的。它只需要一些正则表达式之外的工作。
  • 请说明您需要什么:从21, 2, '23.5R25 ETADT', 'description, with a comma' 仅获取3 个子字符串:212'23.5R25 ETADT'?
  • 我已经编辑了这个问题,但我在这个问题中的目标是能够理解如何在同一行上锚定多个匹配项。 没有找到一个正则表达式可以解决 Python 中缺少 \G 的问题。
  • 是的,完全一样。当我说在 Python 中是可能的时,这就是我所说的。该函数可以很容易地修改为仅在不匹配的情况下返回字符串的其余部分,在这种情况下,我们将拥有我正在寻找的确切行为。我只是很生气,我们必须为 Perl 的正则表达式实现中的某些东西编写一个函数,这在很久以前就已经存在了。
  • @Everyone 我接受了这个答案,因为编辑提供了 Python 最接近于模拟 Perl 的 \G 功能的东西。 \G 是在 Perl 5 中引入的。Which occurred in 1994我们怎么没有这个功能?
猜你喜欢
  • 2021-08-24
  • 1970-01-01
  • 1970-01-01
  • 2016-06-05
  • 1970-01-01
  • 1970-01-01
  • 2011-06-02
  • 2021-03-29
  • 2016-01-06
相关资源
最近更新 更多