2012-08-12 67 views
1

我試圖做一個非貪婪負的比賽,我需要捕獲它。我在Python中使用這些標誌,re.DOTALL | re.LOCALE | re.MULTILINE,做一些文本文件「數據庫」,其中每個領域開始與一個反斜槓一個新行的多行清理。每條記錄都以\ lx字段開頭。捕獲在Python正則表達式匹配負

\lx foo 
\ps n 
\nt note 1 
\ps v 
\nt note 
\ge happy 
\nt note 2 
\ge lonely 
\nt note 3 
\ge lonely 
\dt 19/Dec/2011 

\lx bar 
... 

我想確保每個\ GE領域有它上面某處\ PS場的紀錄,一對一內。目前,一個\ ps後面往往有幾個\ ge,因此需要複製下來,就像上面兩個孤獨的\ ge一樣。

這裏最需要的邏輯:之後的任何\ PS場,但遇到另一個\ PS或\ LX之前,找到\ GE,然後找到另一個\ GE。捕獲所有內容,以便可以將\ ps字段複製到第二個\ ge之前。

這是我的非功能性的嘗試。替換此:

^(\\ps\b.*?\n)((?!^\\(ps|lx)*?)^(\\ge.*?\n)((?!^\\ps)*?)^(\\ge.*?\n) 

與此:

\1\2\3\4\1\5 

我甚至在一個很小的文件(34線長),得到一個內存錯誤。當然,即使這個工作,我不得不多次運行它,因爲它只是試圖處理第二\ GE,而不是第三或第四個。所以這方面的任何想法都會讓我感興趣。

更新:艾倫摩爾的解決方案效果很好,雖然有些案例需要稍微調整。可悲的是,我不得不關閉DOTALL否則我不能防止第一*包括後續\ PS域 - 甚至與非貪婪。*?形成。但我很高興在正則表達式點信息中瞭解了(?s)修飾符。這允許我關閉DOTALL,但仍然在其他正則表達式中使用它,它對於必不可少的。

這是建議的正則表達式,凝聚到單行格式,我需要:

^(?P<PS_BLOCK>(?P<PS_LINE>\\ps.*\n)(?:(?!\\(?:ps|lx|ge)).*\n)*\\ge.*\n)(?P<GE_BLOCK>(?:(?!\\(?:ps|lx|ge)).*\n)*\\ge.*\n) 

這工作,但是當我修改上面的例子中,插入上面的「記2」的\ PS。它也將\ lxs和\ ge2視爲\ lx和\ ge(需要一些\ b)。所以,我去一個稍微調整了版本:

^(?P<PS_BLOCK>(?P<PS_LINE>\\ps\b.*\n)(?:(?!\\(?:ps|lx|ge)\b).*\n)*\\ge\b.*\n)(?P<AFTER_GE1>(?:(?!\\(?:ps|lx|ge)\b).*\n)*)(?P<GE2_LINE>\\ge\b.*\n) 

這種替換字符串:

\g<PS_BLOCK>\g<AFTER_GE1>\g<PS_LINE>\g<GE2_LINE> 

再次感謝!

+0

你試圖做的事是不可能的與一個常規的語言(*「上面的某處」*種類的那樣)。您應該簡單地編寫一個解析器或其他東西,並在運行中創建正確的輸出。 – poke 2012-08-12 20:32:27

+1

我認爲這是正則表達式是錯誤的工具。 – MRAB 2012-08-12 22:08:07

+0

我同意這是推動極限,但看到我回答比約恩的答案是因爲我的原因。 – 2012-12-13 16:32:11

回答

1

因爲您使用了DOTALL標誌,所以出現內存錯誤。如果您的數據按照您展示的方式進行格式化,則無論如何您都不需要該標誌;默認行爲正是你想要的。您不需要非貪婪修飾符(?)。

試試這個正則表達式:

prog = re.compile(r""" 
    ^
    (?P<PS_BLOCK> 
     (?P<PS_LINE>\\ps.*\n) 
     (?:     # Zero or more lines that 
     (?!\\(?:ps|lx|ge)) # don't start with 
     .*\n    # '\ps', '\lx', or '\ge'... 
    )* 
     \\ge.*\n    # ...followed by a '\ge' line. 
    ) 
    (?P<GE_BLOCK> 
     (?:     # Again, zero or more lines 
     (?!\\(?:ps|lx|ge)) # that don't start with 
     .*\n    # '\ps', '\lx', or '\ge'... 
    )* 
     \\ge.*\n    # ...followed by a '\ge' line. 
    ) 
    """, re.MULTILINE | re.VERBOSE) 

替換字符串是:

r'\g<PS_BLOCK>\g<PS_LINE>\g<GE_BLOCK>' 

你還要做多遍。如果Python支持\G,那就沒有必要,但是您可以使用subn並檢查number_of_subs_made的返回值。

+0

謝謝!只需一個小小的調整,對我來說非常有用。我在上面的問題的更新中添加了具體內容。我確實喜歡你詳細記錄的正則表達式,順便說一句,但不幸的是由於我的文本文件格式不得不將它壓縮成一行。 – 2012-08-13 15:18:25

1

任何您遇到問題,使用regexen並告訴自己一次:「我不得不多次運行它,..」那就是你需要寫一個解析器:-)

語言似乎是一個明顯的跡象是相當有規律,所以AA解析器應該很容易寫,也許一樣簡單的東西開始的:

def parse_line(line): 
    kind, value = line.split(' ', 1) # split on the first space 
    kind = kind[1:]     # remove the \ 
    parsed_value = globals().get('parse_' + kind, lambda x:x)(value) 
    return (kind, parsed_value) 

def parse_dt(value): 
    val = ... # create datetime.date() from "19/Dec/2011" 
    return val 

它也許有點太可愛了使用globals()寫一個狀態機,也可節省一噸樣板代碼...:-)

將您輸入到元組的列表:

records = [parse_line(line) for line in open("myfile.dta")] 

搞清楚,如果總是有("ps", ..)元組前("ge", ..)元組應該那麼是很容易 - 例如通過首先注意到所有的lx元組是...

+0

等:看起來很華麗。我想在這裏使用正則表達式(即使代碼更簡單)的原因是我將一組有序的正則表達式存儲在文本文件中。所以,我提供了一個腳本,用戶可以運行甚至自定義腳本,並帶有描述和簡單的en /禁用每個正則表達式的方法,因此即使不知道正則表達式也可以自定義。 (用純文本不需要轉義字符。)我所做的其他正則表達式更加合理。幾個使用。*多行;因此DOTALL。 – 2012-08-13 13:57:33