代码之家  ›  专栏  ›  技术社区  ›  raphink

在python中与finditer()重叠匹配

  •  2
  • raphink  · 技术社区  · 16 年前

    我正在使用regex来匹配文本中的圣经经文参考。当前regex是

    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()返回所有匹配项,即使它们重叠,或者有没有一种方法可以改进我的regex,使其与最后一位正确匹配?

    谢谢。

    1 回复  |  直到 16 年前
        1
  •  4
  •   kennytm    16 年前

    使用的字符已被使用,不应要求regex引擎返回。

    从你的例子中,诗句部分(例如 :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)
    

    (如果你要写这样一个巨大的正则表达式,请使用 /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')]
    

    你可以把这个想法扩展到你的正则表达式。