簡體   English   中英

正則表達式掛起程序(100% CPU 使用率)

[英]Regular expression hangs program (100% CPU usage)

當我使用以下字符串作為正則表達式的輸入時,Java 掛起 100% 的 CPU 使用率。

使用正則表達式:

這是我的應用程序中用於描述字段的正則表達式。

^([A-Za-z0-9\\-\\_\\.\\&\\,]+[\\s]*)+

用於測試的字符串:

來自 Provider_One 的 SaaS 服務 VLAN
第二次嘗試使用 Didier SPT,因為他給我的第一個是錯誤的:-(

當我將相同的字符串拆分為不同的組合時,它可以正常工作。 就像“來自 Provider_One 的 SaaS 服務 VLAN”、“他給我的第一個是錯誤的:-(”等。Java 僅針對上面給定的字符串掛起。

我還嘗試如下優化正則表達式。

^([\\w\\-\\.\\&\\,]+[\\s]*)+

即使這樣也行不通。

災難性回溯的另一個經典案例。

您有嵌套的量詞,當正則表達式到達:輸入字符串時,這些量詞會導致檢查大量的排列,這不是字符類的一部分(假設您使用的是.matches()方法)。

讓我們將問題簡化為這個正則表達式:

^([^:]+)+$

這個字符串:

1234:

正則表達式引擎需要檢查

1234    # no repetition of the capturing group
123 4   # first repetition of the group: 123; second repetition: 4
12 34   # etc.
12 3 4 
1 234
1 23 4
1 2 34
1 2 3 4

......這只是四個字符。 在您的示例字符串中,RegexBuddy 在 100 萬次嘗試后中止。 Java 會很高興地繼續努力......在最終承認這些組合中沒有一個允許以下:匹配之前。

你怎么解決這個問題?

您可以使用所有格量詞禁止正則表達式回溯:

^([A-Za-z0-9_.&,-]++\\s*+)+

將使正則表達式更快地失敗。 順便說一句,我刪除了所有那些不必要的反斜杠。

編輯:

一些測量:

在字符串"was wrong:-)"上,RegexBuddy 需要 862 步才能找出不匹配項。
對於"me was wrong:-)" ,它是 1,742 步。
對於"gave me was wrong:-)" ,14,014 步。
對於"he gave me was wrong:-)" ,28,046 步。
對於"one he gave me was wrong:-)" ,112,222 步。
對於"first one he gave me was wrong:-)" ,>1,000,000 步。

首先,您需要意識到您的正則表達式無法匹配提供的輸入字符串。 字符串包含許多不是“單詞”字符的字符( '<' '>' '/' ':'')' )。

那么為什么要花這么長時間呢?

基本上是“災難性的回溯”。 更具體地說,正則表達式的重復結構為正則表達式回溯算法提供了指數級的備選方案!

這是您的正則表達式所說的:

  1. 一個或多個單詞字符
  2. 后跟零個或多個空格字符
  3. 重復前 2 個模式,次數不限。

問題出在“零個或多個空格字符”部分。 第一次,匹配器將匹配第一個意外字符(即'<' )之前的所有內容。 然后它會退后一點,然后用不同的選擇再試一次……在最后一個字母之前涉及“零空格”,然后當失敗時,它將“零空格”移回一個位置。

問題在於,對於具有N個非空格字符的字符串,有N個不同的地方可以匹配“零空格”,這使得2^N種不同的組合。 隨着N的增長,它迅速變成一個巨大的數字,最終結果很難與無限循環區分開來。

為什么要將空格與其他字符分開匹配? 為什么你在開始時錨定比賽,而不是在結束時? 如果你想確保字符串不以空格開頭或結尾,你應該這樣做:

^[A-Za-z0-9_.&,-]+(?:\s+[A-Za-z0-9_.&,-]+)*$

現在正則表達式引擎只能通過字符串采用一條“路徑”。 如果它在到達末尾之前用完了匹配[A-Za-z0-9_.&,-]的字符,並且下一個字符不匹配\s ,它會立即失敗。 如果它到達末尾但仍匹配空白字符,則它會失敗,因為它需要在每次運行空白字符后至少匹配一個非空白字符。

如果你想確保只有一個空白字符分隔非空白的運行,只需從\s+中刪除量詞:

^[A-Za-z0-9_.&,-]+(?:\s[A-Za-z0-9_.&,-]+)*$

如果您不關心空格相對於非空格的位置,只需將它們全部與相同的字符類匹配:

^[A-Za-z0-9_.&,\s-]+$

我假設你知道你的正則表達式不會匹配給定的輸入,因為:(在笑臉中,你只是想知道為什么它需要這么長時間才能失敗。

當然,由於您正在以 Java 字符串文字的形式創建正則表達式,因此您會這樣寫:

"^[A-Za-z0-9_.&,-]+(?:\\s+[A-Za-z0-9_.&,-]+)*$"

要么

"^[A-Za-z0-9_.&,-]+(?:\\s[A-Za-z0-9_.&,-]+)*$"

要么

"^[A-Za-z0-9_.&,\\s-]+$"

(我知道你在原來的問題中有雙反斜杠,但這可能只是為了讓它們正確顯示,因為你沒有使用 SO 出色的代碼格式化功能。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM