2014-09-03 23 views
1

我想從大文件中提取一些鍵值對加上它們的前面的文本,但使用的正則表達式運行得非常慢,所以它需要優化。自定義鍵值對的正則表達式的優化

輸入包括被1或2的鍵 - 值對相當短串,像

one two three/1234==five/5678 some other text

one two three/1234==five/5678 some other text four/910==five/1112 more text

使用的(顯然不理想的)的正則表達式是

(.*?)\s*([^ /]+)\s*/\s*([\d]+)\s*==\s*([^ /]+)\s*/\s*([\d]+)\s*

(空間可以在字符串中出現在許多領域,因此重複\s*元素。)

樣本代碼來測試上述:

public static void main(String[] args) { 
    String text = "one two three/1234==five/5678 some other text"; 
    text = "one two three/1234==five/5678 some other text four/910==five/1112 more text"; 
    String regex = "(.*?)\\s*([^ /]+)\\s*/\\s*([\\d]+)\\s*==\\s*([^ /]+)\\s*/\\s*([\\d]+)\\s*"; 
    Matcher matcher = Pattern.compile(regex).matcher(text); 
    int end = 0; 
    System.out.println("--------------------------------------------------"); 
    while (matcher.find()) { 
     System.out.println("\"" + matcher.group(1) + "\""); 
     System.out.println(matcher.group(2) + " == " + matcher.group(3)); 
     System.out.println(matcher.group(4) + " == " + matcher.group(5)); 
     end = matcher.end(); 
     System.out.println("--------------------------------------------------"); 
    } 
    System.out.println(text.substring(end).trim()); 
    } 

輸出是鍵 - 值對,再加上前面的文本(所有提取的字段都是必需的)。例如,對於較長的字符串,輸出爲:

-------------------------------------------------- 
"one two" 
three == 1234 
five == 5678 
-------------------------------------------------- 
"some other text" 
four == 910 
five == 1112 
-------------------------------------------------- 
more text 

換句話說,該matcher.find()方法1或2輪運行時,根據該字符串是否具有短或長的形式(1或2鍵 - 值對)。

問題是提取速度很低,有時根據輸入字符串的變化,find()方法需要很長時間才能完成。

對於正則表達式,有沒有更好的形式來顯着加快處理速度?

回答

1

(.*?)放在正則表達式的開頭永遠不是一個好主意。

首先,它可能會很慢。儘管理論上非貪婪的匹配可以被有效地處理(例如參見Russ Cox的re2實現),但很多正則表達式實現並不能很好地處理非貪婪匹配,尤其是在查找操作失敗的情況下。我不知道Java正則表達式實現是否屬於這個類別,但沒有理由誘惑命運。

其次,它沒有意義。正則表達式搜索的語義是找到第一個可能的匹配,這與.*?的語義相同。要獲取捕獲(.*?),只需要從前一個匹配結束(或字符串的開始)到當前匹配開始的子字符串。這是微不足道的,尤其是因爲你已經在跟蹤上一場比賽的結束了。

+0

在這種情況下,它似乎不是導致速度慢的原因。爲什麼要找到()慢,爲200-300字符長的字符串?無論如何。 :-) – PNS 2014-09-04 09:28:26

1

你是如何閱讀文件的?如果您逐行讀取文件BufferedReader#readLine()Scanner#nextLine(),則只需將\G添加到正則表達式的開頭。它首次應用正則表達式時的行爲類似\A,將匹配錨定到字符串的開頭。如果該比賽成功,則下一個find()將停留在前一場比賽結束的位置。如果沒有找到匹配的匹配,它會放棄,並且不會在該字符串中查找更多匹配項。

編輯:我假設你想匹配的每個序列,無論是一個鍵/值對還是兩個,都是在它自己的行上。如果您一次只讀一行文件,您可以在每行上的問題中運行代碼。

至於爲什麼你的正則表達式太慢了,這是因爲正則表達式引擎在放棄之前必須在每個非匹配行上進行多次匹配嘗試 - 可能有數百次。如果認識到某條生產線上的第一次嘗試失敗,那麼在該生產線上進一步的嘗試就不會有什麼好處。所以它會向前推進一個位置並再次嘗試。它始終如一地爲整條線路做好準備。

如果您只希望每行有一個匹配,那麼我會說使用起始行錨點(在MULTILINE模式下爲^)。

+0

在正則表達式的開頭添加\ G使得find()失敗。 – PNS 2014-09-04 19:05:55

+0

你一次處理一行嗎?看到我的擴展答案。 (對不起,延遲;我離線了一段時間 – 2014-09-05 18:57:56

+0

文件逐行處理,所有行與正則表達式匹配,每行總是有1或2個匹配,感謝回覆,隨時: ) – PNS 2014-09-06 02:01:14