Skip to main content
 首页 » 编程设计

python之在 Python 中使用 finditer() 进行重叠匹配

2024年08月12日32mq0036

我正在使用正则表达式来匹配文本中的圣经经文引用。当前的正则表达式是

REF_REGEX = re.compile(''' 
  (?<!\w)                        # Not preceded by any words 
  (?P<quote>q(?:uote)?\s+)?      # Match optional 'q' or 'quote' followed by many spaces 
  (?P<book>                            
    (?:(?:[1-3]|I{1,3})\s*)?     # Match an optional arabic or roman number between 1 and 3. 
    [A-Za-z]+                    # Match any alphabetics 
  )\.?                           # Followed by an optional dot 
  (?:                          
    \s*(?P<chapter>\d+)          # Match the chapter number 
    (?: 
      [:\.](?P<startverse>\d+)   # Match the starting verse number, preceded by ':' or '.' 
        (?:-(?P<endverse>\d+))?  # Match the optional ending verse number, preceded by '-' 
    )?                           # Verse numbers are optional 
  ) 
  (?: 
    \s+(?:                       # Here be spaces 
      (?:from\s+)|(?:in\s+)|(?P<lbrace>\())   # Match 'from[:space:]', 'in[:space:]' or '(' 
      \s*(?P<version>\w+)        # Match a word preceded by optional spaces 
      (?(lbrace)\))              # Close the '(' if found earlier 
  )?                             # The whole 'in|from|()' is optional 
  ''', re.IGNORECASE | re.VERBOSE | re.UNICODE) 

这很好地匹配了以下表达式:

"jn 3:16":                           (None, 'jn', '3', '16', None, None, None), 
"matt. 18:21-22":                    (None, 'matt', '18', '21', '22', None, None), 
"q matt. 18:21-22":                  ('q ', 'matt', '18', '21', '22', None, None), 
"QuOTe jn 3:16":                     ('QuOTe ', 'jn', '3', '16', None, None, None), 
"q 1co13:1":                         ('q ', '1co', '13', '1', None, None, None),  
"q 1 co 13:1":                       ('q ', '1 co', '13', '1', None, None, None), 
"quote 1 co 13:1":                   ('quote ', '1 co', '13', '1', None, None, None), 
"quote 1co13:1":                     ('quote ', '1co', '13', '1', None, None, None), 
"jean 3:18 (PDV)":                   (None, 'jean', '3', '18', None, '(', 'PDV'), 
"quote malachie 1.1-2 fRom Colombe": ('quote ', 'malachie', '1', '1', '2', None, 'Colombe'), 
"quote malachie 1.1-2 In Colombe":   ('quote ', 'malachie', '1', '1', '2', None, 'Colombe'), 
"cinq jn 3:16 (test)":               (None, 'jn', '3', '16', None, '(', 'test'), 
"Q   IIKings5.13-58   from   wolof": ('Q     ', 'IIKings', '5', '13', '58', None, 'wolof'), 
"This text is about lv5.4-6 in KJV only": (None, 'lv', '5', '4', '6', None, 'KJV'), 

但是解析失败:

"Found in 2 Cor. 5:18-21 ( Ministers":                    (None, '2 Cor', '5', '18', '21', None, None), 

因为它返回 (None, 'in', '2', None, None, None, None) 而不是。

有没有办法让 finditer() 返回所有匹配项,即使它们重叠,或者有没有办法改进我的正则表达式,使其正确匹配最后一位?

谢谢。

请您参考如下方法:

消耗的字符被消耗了,你不应该要求正则表达式引擎返回。

从你的例子来看,诗句部分(例如 :1)似乎不是可选的。删除它将匹配最后一位。

ref_regex = re.compile(''' 
(?<!\w)                      # Not preceeded by any words 
((?i)q(?:uote)?\s+)?            # Match 'q' or 'quote' followed by many spaces 
( 
    (?:(?:[1-3]|I{1,3})\s*)?    # Match an arabic or roman number between 1 and 3. 
    [A-Za-z]+                   # Match many alphabetics 
)\.?                            # Followed by an optional dot 
(?: 
    \s*(\d+)                    # Match the chapter number 
    (?: 
        [:.](\d+)               # Match the verse number 
        (?:-(\d+))?             # Match the ending verse number 
    )                    # <-- no '?' here 
) 
(?: 
    \s+ 
    (?: 
        (?i)(?:from\s+)|        # Match the keyword 'from' or 'in' 
        (?:in\s+)| 
        (?P<lbrace>\()      # or stuff between (...) 
    )\s*(\w+) 
    (?(lbrace)\)) 
)? 
''', re.X | re.U) 

(如果您要编写这样一个巨大的 RegEx,请使用 /x 标志。)


如果你真的需要重叠匹配,你可以使用前瞻。一个简单的例子是

>>> rx = re.compile('(.)(?=(.))') 
>>> x = rx.finditer("abcdefgh") 
>>> [y.groups() for y in x] 
[('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'e'), ('e', 'f'), ('f', 'g'), ('g', 'h')] 

您可以将这个想法扩展到您的 RegEx。