正则表达式递归过深

3
我将尝试编写一个正则表达式来匹配可选的带引号的值(有效的引号是"'和`)。 规则是两个引号的出现是转义引号。 以下是我想到的正则表达式:
(?P<quote>["'`])?(?P<value>(?(quote)((?!(?P=quote).)|((?=(?P=quote)).){2})*|[^\s;]*))(?(quote)(?P=quote)|)

现在,以下是可读的版本(包含我认为其作用的注释):

(?P<quote>["'`])?                   #named group Quote (any quoting character?)

    (?P<value>                      #name this group "value", what I am interested in
        (?(quote)               #if quoted 
            ((?!(?P=quote).)|((?=(?P=quote)).){2})* #see below
                                    #match either anything that is not the quote
                                    #or match 2 quotes
        |
            [^\s;]*         #match anything that is not whitespace or ; (my seperators if there are no quotes)
        )
    )

(?(quote)(?P=quote)|)               #if we had a leeding quote we need to consume a closing quote

对于未引用的字符串,它可以正常执行;但对于已引用的字符串,则会崩溃并出现以下错误:

    match = re.match(regexValue, line)
  File "****/jython2.5.1/Lib/re.py", line 137, in match
    return _compile(pattern, flags).match(string)
RuntimeError: maximum recursion depth exceeded

我哪里做错了?

编辑:示例输入 => 输出(捕获组“value”(期望)

text    => text
'text'  => text
te xt   => te
'te''xt'=> te''xt   #quote=' => strreplace("''","'") => desired result: te'xt
'te xt' => te xt

编辑2: 在查看时我注意到了一个错误,见下文,但我认为上面的仍然是有效的re +>它可能是Jython的一个bug,但它仍然不能做我想要做的事情:(非常微小的差异,点移出了前瞻组

new:(?P<quote>["'`])?(?P<value>(?(quote)((?!(?P=quote)).|((?=(?P=quote)).){2})*|[^\s;]*))(?(quote)(?P=quote)|)
old:(?P<quote>["'`])?(?P<value>(?(quote)((?!(?P=quote).)|((?=(?P=quote)).){2})*|[^\s;]*))(?(quote)(?P=quote)|)

只使用正则表达式会很混乱。如果你同时使用正则表达式和状态机,会更容易些。你能做到吗? - Wilduck
你有没有考虑使用类似的东西,比如在不同的正则表达式之间使用 | 运算符?例如:"\".*?\"|'.*?'" - Joel Cornett
@JoelCornett:你的意思是(正则表达式引用值)|(正则表达式非引用值)吗?我将不得不写(几乎)相同的表达式四次,每个引用版本一次(3次),以及一次非引用版本。此外,我提取值时会遇到麻烦,因为我必须检查所有捕获组,如果我没有弄错的话。 - ted
@JoelCornett 不幸的是,我必须使用Jython,它是Python 2.5,但谢谢您的输入。为什么正则表达式只返回“Hello”对我来说是个谜。 - ted
1
你可以尝试使用带有适当引用符的CSV模块吗? - jfs
显示剩余5条评论
3个回答

3

根据评论中的建议,我建议明确地列出所有可能性:

r = r"""
    ([^"'`]+)
    |
    " ((?:""|[^"])*) "
    |
    ' ((?:''|[^'])*) '
    |
    ` ((?:``|[^`])*) `
"""

当提取匹配项时,您可以利用只有一个四个组将被填充的事实,并简单地删除所有空组:
r = re.compile(r, re.X)
for m in r.findall(''' "fo""o" and 'bar''baz' and `quu````x` '''):
    print ''.join(m)

0
如果我正确理解了您的问题,您需要处理一个包含三种不同引号的字符串。
“你好,先生”,猴子“漫不经心地”说道。
您想要提取引用的值。在这种情况下,它们将是:
Hello, sir monkey nonchalantly
以下表达式可以提取它们:
>>> expr = "\"(.*?)\"|'(.*?)'|`(.*?)`"

注意:

>>> s = """
"Hello, sir", said the 'monkey', `nonchalantly`. 
"""
>>> import re
>>> m = re.finditer(expr, s)
>>> for match in m:
...     print match.group()
...
('Hello, sir', None, None)
(None, 'monkey', None)
(None, None, 'nonchalantly')

顺便提一下,你的正则表达式在我的 Python 版本(Mac OSX 10.7.4 上的 cPython 2.7.2)上似乎可以工作,但生成错误的结果。


你的正则表达式对 "te""xt","text" 做了什么?我猜它会得到 te""xt","text (贪婪的小正则表达式),而且我不知道是否有非贪婪修饰符。此外,我需要进行转义,我想使用正则表达式匹配,并逐位从前往后消耗字符串并获取 te""xttext - ted
?将贪婪修饰符转换为非贪婪修饰符。 - Joel Cornett
@ted:这个正则表达式将返回“te”,“xt”和“text”。 - Joel Cornett
我忘记了那个,但这仍然不是我想要的(请参见第一条评论)。 - ted

0

我在稍微尝试了一下之后找到了解决方案:

good:(?P<quote>["'`])?(?P<value>(?(quote)((?!(?P=quote)).|((?=(?P=quote)).){2})*|[^;\s]*))(?(quote)(?P=quote)|)
bad :(?P<quote>["'`])?(?P<value>(?(quote)((?!(?P=quote)).|((?=(?P=quote)).){2})*|[^\s;]*))(?(quote)(?P=quote)|)

不,我没有理解它们之间的区别。


@J.F.Sebastian:最后你会发现一个类似于[^;\s]的组,在此之前它是[^\s;]。虽然我同意在技术上这两个应该表现相同,但在我的系统上它们并不相同。请记住,我使用的是Jython 2.5(它已经内置到我必须使用的应用程序中,因此我无法升级),而你没有出现错误,但很好的测试脚本,我可以在周一检查我的正则表达式是否在jython下相同,并可能随后提交错误报告。 - ted
Python 2、3、pypy 和 jython 对我尝试的输入产生相同的结果(在“好”和“坏”之间没有区别)。 - jfs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接