2011-07-26 52 views
3

我寫了一個Ruby腳本來處理大量的文件,請使用以下URI從文檔的字符串表示提取的URI:紅寶石正則表達式掛

#Taken from: http://daringfireball.net/2010/07/improved_regex_for_matching_urls 
URI_REGEX =/
(       # Capture 1: entire matched URL 
    (?: 
    [a-z][\w-]+:    # URL protocol and colon 
    (?: 
     \/{1,3}      # 1-3 slashes 
     |        # or 
     [a-z0-9%]      # Single letter or digit or '%' 
    ) 
    |       # or 
    www\d{0,3}[.]    # "www.", "www1.", "www2." … "www999." 
    |       # or 
    [a-z0-9.\-]+[.][a-z]{2,4}\/ # looks like domain name followed by a slash 
) 
    (?:       # One or more: 
    [^\s()<>]+      # Run of non-space, non-()&lt;&gt; 
    |        # or 
    \(([^\s()<>]+|(\([^\s()<>]+\)))*\) # balanced parens, up to 2 levels 
)+ 
    (?:       # End with: 
    \(([^\s()<>]+|(\([^\s()<>]+\)))*\) # balanced parens, up to 2 levels 
    |         # or 
    [^\s`!()\[\]{};:'".,<>?«»「」‘’]  # not a space or one of these punct chars 
) 
)/xi 

它工作得很好的所有文件的99.9%,但token = "synsem:local:cat:(subcat:SubMot,adjuncts:Adjs,subj:Subj),"

我使用的是標準的Ruby正則表達式oeprator:token =~ URI_REGEX,我沒有得到任何異常或錯誤消息時遇到的文件中加入以下令牌總是掛斷我的腳本。

首先,我試圖解決封裝正則表達式評價爲Timeout::timeout塊的問題,但是這會降低性能來得多。

有關如何解決此問題的任何其他想法?

回答

5

爲什麼重塑the wheel

require 'uri' 
uri_list = URI.extract("Text containing URIs.") 
+0

請看,這正是我的意思。 +1使用正確的工具進行工作。 –

8

你的問題是catastrophic backtracking。我只是將你的正則表達式和你的測試字符串加載到RegexBuddy中,並且它在正則表達式引擎的1.000.000次迭代後放棄了(並且從它的外觀來看,如果它不中止,它會繼續數百萬次)。

問題就出現了,因爲你的文本的某些部分可以通過您正則表達式的不同部分(這是可怕的複雜而痛苦的閱讀)相匹配;看起來你的正則表達式中的「一個或多個:」部分和「結束:」部分在比賽中發生了爭執(當它不工作時),嘗試了數百萬個排列都失敗的排列。

這很難提出一個解決方案,而不知道什麼匹配一個URI的規則是(我沒有)。所有這些括號的平衡表明,正則表達式可能並不適合這項工作。也許你可以打破這個問題。首先使用一個簡單的正則表達式來查找與URI類似的所有內容,然後在第二步中驗證(對於某種類型的Ruby,是否存在URI解析器?)。

你也許能夠做的另一件事是防止正則表達式引擎從使用atomic groups回溯。如果您可以將某些(?:...)組更改爲(?>...)組,那麼允許正則表達式通過禁止回溯到這些組而更快地失敗。然而,這可能會改變比賽,並且在需要回溯來完成比賽的情況下會失敗 - 所以這並不總是一種選擇。

0

URI.extract("Text containing URIs.")如果您只需要URI,是最好的解決方案。

我最終使用pat = URI::Parser.new.make_regexp('http')來獲取內置的URI解析regexp並在match = str.match(pat, start_pos)中使用它來通過URI迭代地解析輸入文本URI。我這樣做是因爲我還需要文本中的URI位置,並且返回的對象將此信息提供給我match.begin(0)