簡體   English   中英

用於長字符串的Java中的正則表達式模式匹配性能

[英]Regex pattern match performance in Java for long string

當找到匹配時我有一個很好的(500納秒)正則表達式,但是當沒有匹配時需要花費很多時間(超過3秒)。 我懷疑這可能是因為回溯。 我嘗試了一些選項,比如將.*轉換為(.*)? 基於一些文檔,但它沒有幫助。

輸入:一個非常長的字符串 - 在某些情況下為5k字符。

正則表達式匹配: .*substring1.*substring2.*

我正在預編譯模式並重新使用匹配器,我還能嘗試什么?

這是我的代碼片段 - 我將使用數百萬個不同的輸入字符串調用此方法,但只是少數幾個正則表達式模式。

private static HashMap<String, Pattern> patternMap = new HashMap<String, Pattern>();
private static HashMap<String, Matcher> matcherMap = new HashMap<String, Matcher>();

這是我的方法:

public static Boolean regex_match(String line, String regex) {
    if (regex == null || line == null) {
      return null;
    }
    if (!patternMap.containsKey(regex)) {
      patternMap.put(regex, Pattern.compile(regex));
      matcherMap.put(regex,patternMap.get(regex).matcher(""));
    }
    return matcherMap.get(regex).reset(line).find(0);
 }

如果使用indexOf() ,則可以驗證模式是否匹配:

int pos1 = str.indexOf("substring1");
int pos2 = str.indexOf("substring2", pos1);

if(pos1 != -1 && pos2 != -1){
  // regex
}

當正則表達式不匹配時,您將獲得災難性的回溯。 事實上,即使有匹配,你的模式很可能會做很多回溯。 .*將占用整個字符串,然后需要向后,不情願地回饋字符。

如果你的字符串看起來像: substring1 substring2........50000 more characters...... ,那么懶惰你會得到更好的表現.*? 請注意(.*)? 是不是.*?

正則表達式的性能取決於子字符串是什么,以及它們匹配的內容。 如果你的字符串看起來像: substring1........50000 more characters...... substring2 ,那么你將獲得更好的性能.*

正如你所暗示的那樣,你的正則表達式會遇到一個被稱為災難性回溯的問題。 基本上,第一個.*將匹配整個字符串,然后回溯直到substring1匹配。 這將與substring2重復。 因為substring2失敗,第二個.*將需要找到substring2開始匹配的另一個地方,然后它將再次失敗。 每次substring1匹配時,我們都需要檢查substring2可能匹配的每個地方。

您已經在使用pattern.find() ,因此您可以省略開始和結束.* 然后,將內部.*更改為.*? 可以通過將貪婪的匹配器變成懶惰來改善性能。

這會產生: substring1.*?substring2

使用String.indexOf()比Regex快得多,如果案例足夠簡單就可以使用它。 您可以將問題重新編碼為:

public static boolean containsStrings(String source, String string1, String string2) {
  long pos1, pos2;
  pos1 = source.indexOf(string1);
  if(pos1 > -1) {
    pos2 = source.indexOf(string2,pos1 + string1.length);
    if(pos2 > pos1 && source.indexOf(string1,pos2 + string2.length) < -1) {
      return true;
    }
  }
  return false;
}

請注意,我的解決方案並不涉及其中的情況下string2包含在string1 ,如果這是你需要的是增加了邏輯的情況下。

^((?!substring1).)*substring1((?!substring2).)*substring2.*?\\Z

應該這樣做是因為包含一個子字符串多次但不是兩個子字符串的字符串不會回溯廣告惡心。 如果您不需要匹配器在輸入結束時結束,則可以在最后刪除。*?\\ Z.

暫無
暫無

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

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