Python處理正則表達式超時的辦法
最近在項目中遇到一個問題,就是需要采用正則匹配一些疑似暗鏈和掛馬的HTML代碼,而公司的老大給的正則表達式有的地方寫的不夠嚴謹,導致在匹配的時候發生卡死的現象,而後面的邏輯自然無法執行了。雖然用正則表達式來判斷暗鏈和掛馬可能不那麽準確或者行業內很少有人那麽做,但是本文不討論如何使用正確的姿勢判斷暗鏈掛馬,只關註與正則超時的處理。
在使用正則表達式的時候,如果正則寫的太糟糕,所消耗的時間是驚人的,並且有可能會一直回溯,而產生卡死的現象,所以一般的大型公司都會有專門的人來對正則進行優化,從而提高程序效率。一般來說如果可能的話不要讓用戶來輸入正則進行匹配。但是現在既沒有專門的人進行正則的優化,本人也對正則了解的不夠,所以只能從另外的角度來考慮處理超時的問題。
首先我想到的方法是另外開啟一個線程來進行匹配,而在主線程中進行等待,如果發現子線程在規定的時間內沒有返回就kill掉子線程。這也是一個方案,但是我現在要介紹另外一種方案,該方案來自我在網上看到的一篇博客.
博客地址
該博客給出了另外一種辦法,就是采用信號的方式,在正則匹配之前定義一個信號,並規定觸犯時間和處理的函數,如果在規定時間內程序沒有結束那麽觸發一個TimeoutError的異常,而主線程收到這個異常時就會中斷執行,並處理這個異常,這樣就從正則匹配中解脫出來,達到了我們要的結果。這個方法有兩個不足之處:
- 信號這個東西是Linux獨有的,在Windows下不適用
- 信號只能在主線程中使用,而如果在子線程中進行正則匹配,那麽這個方法就不適用
我的項目正在運行在Linux系統上,所以針對第一個不足來說可以接受,但是我的正則匹配都是在子線程中,所以乍看之下這個方案也不太靠譜,但是好在我在後面的評論中發現博主給出了針對第二種不足的解決方案——開辟一個子進程,將正則匹配放到子線程中,這樣一來可以充分利用多核(畢竟Python中的多線程是個偽多線程),二來可以分方便的使用該方案解決問題,下面是實際的代碼
import re
import multiprocessing
import signal
def time_out(b, c):
raise TimeoutError
def search_with_timeout(pipe, word, value):
signal.signal(signal.SIGALRM, time_out)
signal.alarm(1)
r = re.compile(word)
try:
ret = r.search(value, re.I)
b_ret = True if ret != None else False
pipe.send(b_ret)
except TimeoutError:
pipe.send(False)
在上面的代碼中先的定義了一個信號,給定1s中以後觸發,觸發的函數為time_out然後執行正則表達式,如果在這1s中內無法完成,那麽處理函數會被調用,會跑出一個異常,此時主線程終止當前任務的執行,進入到異常處理流程,這樣就可以終止正則匹配,從而正常的返回。由於這個部分是一個新進程自然就涉及到不同進程之間的通信,在這個例子中我使用了管道進行通信。由於Python在創建子進程的時候可以進行參數的傳入所以我只需要一個管道將數據從子進程中寫入,再從朱金城中讀取就好了。
下面是調用該子進程的代碼:
pipe = multiprocessing.Pipe()
p = multiprocessing.Process(target = search_with_timeout, args = (pipe[0], word, left_value))
p.start()
p.join() #等待進程的結束
ret = pipe[1].recv() #獲取管道中的數據
Python處理正則表達式超時的辦法