2010-06-12 84 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