2011-04-16 14 views
7

基本上我想要做的是運行多個(15-25)正則表達式替換一個字符串上儘可能最好的內存管理。C#多個正則表達式替換字符串 - 內存太多

概述: 通過ftp附加到StringBuilder來獲取非常大的字符串,僅流文本文件(有時爲html)。文件大小範圍從300KB到30MB。

正則表達式是半複雜的,但需要多行文件(例如識別書的某些部分),所以任意斷開字符串或在每個下載循環中運行替換都不在答案中。

樣本替換:

Regex re = new Regex("<A.*?>Table of Contents</A>", RegexOptions.IgnoreCase); 
source = re.Replace(source, ""); 

用的每次運行更換內存的天空火箭,我知道這是因爲字符串在C#中不可改變的,它需要進行復制 - 即使我打電話GC.Collect()它仍然不足以支持30MB文件。

任何關於更好的方法或執行多個正則表達式的方法都會使用常量內存(使2個副本(如此在內存中爲60MB),執行搜索,放棄拷貝回到30MB)?

更新:

似乎沒有成爲一個簡單的答案,但對於未來的人在看這個,我結束了使用下面的所有答案的組合來得到它可接受的狀態:

  1. 如果可能的話,將字符串拆分爲塊,請參閱manojlds的答案,以便在讀取文件時尋找合適的端點。

  2. 如果你無法拆分流,至少在可能的時候再拆分它 - 請參閱ChrisWue的一些外部工具的答案,這些工具可能有助於這個過程管道到文件。

  3. 優化正則表達式,避免貪婪的操作符,儘量限制引擎必須做的事 - 參見Sylverdrag的答案。

  4. 在可能的情況下合併正則表達式,這會減少正則表達式彼此不相互關聯時的替換次數(在這種情況下,用於清理錯誤的輸入) - 請參閱Brian Reichle對代碼示例的回答。

謝謝大家!

+0

我打電話給每個正則表達式一次在字符串上,將編譯幫助的次數它做替換?就像在示例正則表達式中一樣,如果有500個匹配的目錄替換,編譯後的版本運行速度會更快嗎? – WSkid 2011-04-16 03:52:26

+0

對不起,我意識到自己的錯誤,並刪除了我的評論,但當時您已回覆。是的,編譯可能不會給你帶來好處。 – manojlds 2011-04-16 03:57:07

+0

我不確定該需求是否允許您解析文件行。如果文件中的一行可以獨立考慮,那麼我建議你將每個文件行(CPU vs內存??)而不是整個文件解析到內存中。 CPU週期/時間可能會增加,但我認爲使用的內存會減少。你可以試試看。 – 2011-04-16 04:30:14

回答

2

根據的正則表達式的,你可能會性質能夠將它們組合成單個正則表達式並使用Replace()的重載,它接受MatchEvaluator委託來確定匹配字符串的替換。

Regex re = new Regex("First Pattern|Second Pattern|Super(Mega)*Delux", RegexOptions.IgnoreCase); 

source = re.Replace(source, delegate(Match m) 
{ 
    string value = m.Value; 

    if(value.Equals("first pattern", StringComparison.OrdinalIgnoreCase) 
    { 
     return "1st"; 
    } 
    else if(value.Equals("second pattern", StringComparison.OrdinalIgnoreCase) 
    { 
     return "2nd"; 
    } 
    else 
    { 
     return ""; 
    } 
}); 

當然,如果後面的模式需要能夠匹配先前替換的結果,這會崩潰。

+0

是的,對不起,我忘了補充一些替代品是基於其他替代品。但是,使用這種方法,我可以將幾個組合起來,並減少到約5個替換電話 - 謝謝! – WSkid 2011-04-16 07:53:42

2

看一看這個帖子裏面談到了搜索使用正則表達式流,而不是在消耗內存中的字符串存儲:

http://www.developer.com/design/article.php/3719741/Building-a-Regular-Expression-Stream-Search-with-the-NET-Framework.htm

+0

這當然是一個有趣的閱讀,但如果該模式沒有固定的長度上限,將無濟於事。我能想到的唯一方法就是在下一個字符串開始時掃描一個字符串時保持「狀態」。但是,您還需要堅持過去可能匹配的「頁面」。 – 2011-04-16 04:31:30

+0

與Brian一致,它並不完全一致,但我認爲我會將這種技術與幾個答案結合使用,以便使用此方法的最佳中斷機會並將其保存到文件中在ChrisWue的回答中提到。隨着你的答案布萊恩它把一切都變成了一個更易於管理的狀態。 – WSkid 2011-04-16 08:09:20

1

假設您加載的文檔有一些結構的類型,您最好寫一個解析器將文檔放入結構化表單中,將大型字符串拆分爲多個塊,然後在該結構上進行操作。

大字符串的一個問題是超過85,000字節的對象被認爲是大對象,並放在未壓縮的大對象堆上,並且可能導致意外的內存不足情況。

另一種選擇是通過外部工具(如sedawk)進行管道傳輸。

+0

謝謝你的第二部分 - 帶領我看看這個錯誤報告,說LOH確實是零散的,在.Net 2和3.5中有潛在的OOM錯誤,但在4.0中稍微好一點:http://connect.microsoft.com/ VisualStudio/feedback/details/521147/large-object-heap-fragmentation-causes-outofmemoryexception – WSkid 2011-04-16 07:47:44

2

我有一個相當類似的情況。

使用編譯選項的正則表達式:

Source = Regex.Replace(source, pattern, replace, RegexOptions.Compiled); 

根據您的情況,它可以使速度有很大的差異。

不是一個完整的解決方案,尤其是對於大於3-4 Mb的文件。

如果你決定應該運行哪個正則表達式(不是我的情況),你應該儘可能地優化正則表達式,避免代價高昂的操作。例如,避免不熟練的操作員,避免向前看和後面看。

代替使用:

<a.*?>xxx 

使用

<a[^<>]*>xxx 

原因是,一個ungreedy操作迫使正則表達式引擎相比,表達式的其餘部分,以檢查每個字符,而[^ <>]僅需要將當前字符與<和>進行比較,並在條件匹配後立即停止。在一個大文件上,這可能會導致半秒和應用程序凍結之間的差異。

它並不完全解決問題,但它應該有所幫助。

+0

這個第二個消息確實有很大的幫助,現在回頭看它是一個非常明顯的優化 - 從粗略測試改變了約8個不懂的東西。 star to the not <>將計算時間減少了約2分鐘。 10分鐘的工作。 – WSkid 2011-04-16 11:05:02

+0

@WSkid:2分鐘下來並不糟糕。你也嘗試編譯過嗎?對於大文件,它應該得到回報。它在這裏有很大的不同。 – Sylverdrag 2011-04-16 13:00:11