2010-06-12 88 views
2

我使用正则表达式来匹配文本中的圣经经文引用。目前正则表达式是与Python中的finditer()重叠匹配

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()返回所有匹配,即使它们重叠,还是有一种方法来提高我的正则表达式,以便它正确匹配最后一位?

谢谢。

+2

什么是疯狂的正则表达式... – kennytm 2010-06-12 06:19:39

+0

是的,但它工作正常(除了最后一点) – 2010-06-12 06:25:27

+3

对于...的爱...怜悯那些在你身后必须保持这种状态的穷人(或者只是你自己在几年内)。将其分解为单独搜索不同*类型*的引用。 – 2010-06-12 06:57:18

回答

4

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

从您的示例中,经文部分(例如: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')] 

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

+0

谢谢,这真的很有用,我会用re.X格式的正则表达式更新我的问题。 – 2010-06-12 07:08:36

+0

先行解决方案解决了我的问题,但后来我无法再获取组(0)。 我把这个lookahead放在书名后面的整个块(围绕'\。?'后面的所有内容)。现在,当我尝试匹配'jn 3:16'之类的东西时,我得到: >>> REF_REGEX.search(“jn 3:16”)。groups() (None,'jn','3', '16',None,None,None) >>> REF_REGEX.search(“jn 3:16”)。group(0) 'jn' 我不明白为什么group(0)没有返回整个匹配的字符串。 – 2010-06-12 08:46:34

+1

@Rap:前瞻部分不计入比赛。如果你需要整个匹配,你需要使用'foo(?=(bar(etc)))'和'group(0)+ group(1)'。 – kennytm 2010-06-12 09:23:57