无法使用 PyPi 正则表达式包将数字范围与 DEFINE 块中声明的模式匹配

Failing to match number ranges with pattern declared in DEFINE block using PyPi regex package

我正在使用 https://github.com/mrabarnett/mrab-regex(通过 pip install regex,但在此处遇到故障:

pattern_string =  r'''
        (?&N)
        ^ \W*? ENTRY              \W* (?P<entries>    (?&Range)    )     (?&N)

        (?(DEFINE)
             (?P<Decimal>
                 [ ]*? \d+ (?:[.,] \d+)? [ ]*?
             )
             (?P<Range>
                 (?&Decimal) - (?&Decimal) | (?&Decimal)
                 #(?&d) (?: - (?&d))?
             )
             (?P<N>
                 [\s\S]*?
             )
        )
    '''

flags = regex.MULTILINE | regex.VERBOSE  #| regex.DOTALL  | regex.V1 #| regex.IGNORECASE | regex.UNICODE

pattern = regex.compile(pattern_string, flags=flags)

bk2 = f'''
ENTRY: 0.0975 - 0.101
'''.strip()
match = pattern.match('ENTRY: 0.0975 - 0.101')
match.groupdict()

给出:

{'entries': '0.0975', 'Decimal': None, 'Range': None, 'N': None}

它错过了第二个值。

> pip show regex
Name: regex
Version: 2022.1.18
Summary: Alternative regular expression module, to replace re.
Home-page: https://github.com/mrabarnett/mrab-regex
Author: Matthew Barnett
Author-email: regex@mrabarnett.plus.com
License: Apache Software License
Location: ...
Requires:
Required-by:

> python --version
Python 3.10.0

问题是你在Decimal组模式中定义的space被消耗了,而DEFINE模式是原子的,所以虽然最后的[ ]*?部分是惰性的,可以匹配零次,一旦匹配,就没有回头路了。如果将 Decimal 模式放入原子组并比较两个模式,则可以检查这一点,参见。 this regex demo and this regex demo(?mx)^\W*?ENTRY\W*(?P<entries>(?>[ ]*? \d+ (?:[.,] \d+)? [ ]*?) - (?>[ ]*? \d+ (?:[.,] \d+)? [ ]*?) | (?>[ ]*? \d+ (?:[.,] \d+)? [ ]*?)) 使用 DEFINE 块公开与正则表达式相同的行为,而 (?mx)^\W*?ENTRY\W*(?P<entries>[ ]*? \d+ (?:[.,] \d+)? [ ]*? - [ ]*? \d+ (?:[.,] \d+)? [ ]*? | [ ]*? \d+ (?:[.,] \d+)? [ ]*?) 正确找到匹配项。

最简单的解决方法是将可选的 space 模式移动到 Range 组模式中。

您可能还想在此处介绍其他小的增强功能:

  • 由于您只对捕获的子字符串感兴趣,因此不需要使用 regex.matchN 组模式 ([\s\S]*?),您可以使用 regex.search 并从正则表达式
  • 中删除 N 模式
  • 你不需要为 a|a-b 这样的模式使用组,你可以使用更有效的可选 non-capturing 组方法,a(?:-b)?.

所以,正则表达式可以看起来像

^ \W* ENTRY              \W* (?P<entries>    (?&Range)    ) 
(?(DEFINE)
    (?P<Decimal>
        \d+ (?:[.,] \d+)?
    )
    (?P<Range>
        (?&Decimal)(?:\ *-\ *(?&Decimal))*
    )
)

参见 regex demo.

参见 Python demo:

import regex
pattern_string =  r'''
        ^ \W* ENTRY              \W* (?P<entries>    (?&Range)    )

        (?(DEFINE)
             (?P<Decimal>
                 \d+ (?:[.,] \d+)?
             )
             (?P<Range>
                 (?&Decimal)(?:\ *-\ *(?&Decimal))?
             )
        )
    '''

flags = regex.MULTILINE | regex.VERBOSE
pattern = regex.compile(pattern_string, flags=flags)

bk2 = f'''
ENTRY: 0.0975 - 0.101
'''.strip()

match = pattern.search('ENTRY: 0.0975 - 0.101')

print(match.groupdict())

输出:

{'entries': '0.0975 - 0.101', 'Decimal': None, 'Range': None}