【问题标题】:Extracting ALL matches of a nested regular expression in python在python中提取嵌套正则表达式的所有匹配项
【发布时间】:2009-10-27 22:16:53
【问题描述】:

我正在尝试解析满足 python 正则表达式的项目列表

r'\A(("[\w\s]+"|\w+)\s+)*\Z'

也就是说,它是一个空格分隔的列表,除了带引号的字符串中允许有空格。我想获取列表中的项目列表(即与

r'("[\w\s]+"|\w+)'

部分。所以,例如

>>> parse('foo "bar baz" "bob" ')
['foo', '"bar baz"', '"bob"']

有没有什么好的方法可以用 python re 做到这一点?

很多事情都不太行。例如

>>> re.match(r'\A(("[\w\s]+"|\w+)\s+)*\Z', 'foo "bar baz" "bob" ').group(2)
'"bob"'

只返回它匹配的最后一个。另一方面

>>> re.findall(r'("[\w\s]+"|\w+)', 'foo "bar baz" "bob" ')
['foo', '"bar baz"', '"bob"']

但它也接受格式错误的表达式,例如

>>> re.findall(r'("[\w\s]+"|\w+)', 'foo "bar b-&&az" "bob" ')
['foo', 'bar', 'b', 'az', '" "', 'bob']

那么有什么方法可以使用原始正则表达式并获取与第 2 组匹配的所有项目?类似的东西

>>> re.match_multigroup(r'\A(("[\w\s]+"|\w+)\s+)*\Z', 'foo "bar baz" "bob" ').group(2)
['foo', '"bar baz"', '"bob"']
>>> re.match_multigroup(r'("[\w\s]+"|\w+)', 'foo "bar b-&&az" "bob" ')
None

编辑:保留输出中的引号很重要,因此我不想要

>>> re.match_multigroup(r'\A(("[\w\s]+"|\w+)\s+)*\Z', 'foo "bar baz" "bob" ').group(2)
['foo', 'bar baz', 'bob']

因为那时我不知道是否引用了 bob。

【问题讨论】:

    标签: python regex parsing


    【解决方案1】:

    我不认为正则表达式是正确的工具。试试csv 模块:

    >>> s = 'foo "bar baz" "bob" '
    >>> for i in csv.reader([s], delimiter=' '):
        print(i)
    
    
    ['foo', 'bar baz', 'bob', '']
    

    【讨论】:

    • 感谢您的建议。但是在我的应用程序中,我需要区分被引用的项目和没有被引用的项目,因为这似乎没有这样做。
    • 这个要求的目的是什么?
    • 我正在解析一个规范。规范是两种类型的事物(未引用的事物)和(引用的事物)的列表。它们需要区别对待,因此需要区分。如果您喜欢将 " 替换为 * 或 & 或其他字符,则它只是对第二种类型的事物进行定界。对不起,非标准符号。
    • 你不知道应该从订单中引用哪个吗?
    • 不,它们可以按任何顺序排列。例如: 'foo bob ' 'foo "bob" ' '"foo" bob ' '"foo" "bob" ' 都是应该相互区分的有效输入
    【解决方案2】:

    这里有一个解决方案,可以拆分不在一对引号内的任何空格:

    re.split('\s+(?=[^"]*(?:"[^"]*"[^"]*)*$)', target)
    

    只有在刚刚匹配的空格前面有偶数个引号时,前瞻才会成功。如果文本中的引用部分可以包含转义引号,则可能需要更复杂的正则表达式,具体取决于转义的完成方式。

    【讨论】:

    • 哇,这似乎行得通,现在如果我能弄清楚它是如何工作的:)
    • 呃,其实有一个问题,this永远不会拒绝表达式,所以如果target = 'foo"bar baz" "bob"',它返回['foo"bar baz"', '"鲍勃"']。
    • 我想我可以检查每个输出的格式是否正确,如果不是,则失败。
    • 您的输入真的包含引用和未引用的部分吗?你的例子都没有。
    • 不,不应该。但是有人可能会打错字,所以我想拒绝不符合正则表达式的字符串。因此,我的带引号和不带引号的字符串粘在一起的例子应该被拒绝。同样,应该拒绝像 '&&' 这样的其他表达式,因为它们使用了不允许的字符。您的表达式不会验证输入是否正确,如果输入正确,它只是将它们正确分开。
    【解决方案3】:

    好的,我最终决定分两步进行。

    首先,我检查表达式在语法上是否有效,然后我将其分解为各个部分:

    def parse(expr):
        if re.match(r'\A(("[\w\s]+"|\w+)\s+)*\Z', expr):
            return re.findall(r'("[\w\s]+"|\w+)', expr)
    

    所以:

    >>> parse('foo "bar baz" "bob" ')
    ['foo', '"bar baz"', '"bob"']
    >>> parse('foo "bar b-&&az" "bob" ')
    >>> parse('foo "bar" ')
    ['foo', '"bar"']
    >>> parse('"foo" bar ')
    ['"foo"', 'bar']
    >>> parse('foo"bar baz" "bob" ')
    >>> parse('&&')
    

    我大约 90% 确定此方法适用于所有字符串,但如果有人有更通用的解决方案,我仍然会感兴趣,这对我来说似乎有点笨拙。

    感谢 SilentGhost 和 Alan Moore 的帮助。我以前不知道 python csv 或正则表达式前瞻,了解这些可能对我有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-12
      • 1970-01-01
      • 1970-01-01
      • 2013-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多