簡體   English   中英

StringBuilder 中的正則表達式替換

[英]Regex replacements inside a StringBuilder

我正在將文本文件的內容寫入 StringBuilder,然后我想使用正則表達式對 StringBuilder 中包含的文本執行許多查找/替換操作。

我遇到了一個問題,因為 StringBuilder 替換函數無法接受正則表達式參數。

我可以在普通字符串上使用 Regex.Replace 但我的印象是這是低效的,因為需要在內存中創建字符串的兩個副本,因為 .net 字符串是不可變的。

更新文本后,我計划將其寫回原始文件。

解決我的問題的最佳和最有效的方法是什么?

編輯

除了下面的答案之外,我還發現以下問題也對我的問題有所了解 -

你的時間最好和最有效的解決辦法是先試試最簡單的方法:忘記StringBuilder ,只是使用Regex.Replace 然后找出它有多慢 - 它可能已經足夠好了。 不要忘記在編譯和非編譯模式下嘗試正則表達式。

如果這還不夠快,請考慮使用StringBuilder進行任何您可以簡單表達的替換,然后使用Regex.Replace進行其余的替換。 您可能還想考慮嘗試組合替換,減少使用的正則表達式(以及中間字符串)的數量。

您有 3 個選擇:

  1. 正如其他人在此處推薦的那樣,使用字符串以低效的方式執行此操作。

  2. Regex對象上使用.Matches()調用,並模擬.Replace()工作方式(參見 #3)。

  3. 調整正則Regex的 Mono 實現以構建接受StringBuilder的正則Regex 幾乎所有的工作都已經在 Mono 中為您完成,但需要時間來確定使其在自己的庫中工作的部分。 奇怪的是,Mono 的Regex Novell 2002 年的 JVM 實現Regex

擴展上述內容:

2. 模擬 Replace()

您可以通過調用.Matches()來模擬LTRReplace的行為,跟蹤您在原始字符串中的位置並循環:

var matches = regex.Matches(original);
var sb = new StringBuilder(original.Length);
int pos = 0; // position in original string
foreach(var match in matches)
{
    // Append the portion of the original we skipped
    sb.Append(original.Substring(pos, match.Index));
    pos = match.Index;

    // Make any operations you like on the match result, like your own custom Replace, or even run another Regex

    pos += match.Value.Length;
}
sb.Append(original.Substring(pos, original.Length - 1));

但是,這只會為您節省一些字符串 - Mono 方法是唯一一種真正徹底消除字符串的方法。

3. 單聲道

這個答案自 2014 年以來一直沒有出現,我從未在評論或搜索中看到基於 StringBuilder 的正則表達式。 所以,為了讓球滾動,我從 Mono 中提取了 Regex impl 並將其放在這里:

https://github.com/brass9/RegexStringBuilder

然后,我創建了一個接口IString以允許更松散地傳遞輸入和輸出 - stringStringBuilderchar[]每個都包裝在一個實現 IString 的類中。

結果並不快 - Microsoft 高度優化的代碼運行 10,000 次簡單替換比此代碼快約 6 倍。 但是,我沒有做任何優化它,特別是在消除底層代碼中更深層次的字符串(它在某些情況下轉換為字符串以運行 .ToLower() 僅返回到字符數組)。

歡迎投稿。 下面討論了 2014 年的代碼如何在 Mono 中工作(在它從 Mono 中刪除之前不久,對於 Microsoft 的基於字符串的實現):

System.Text.RegularExpressions.Regex使用RxCompiler以 RxInterpreterFactory 的形式實例化 IMachineFactory , RxInterpreterFactory ,這使IMachine成為RxInterpreter 讓它們發出是您需要做的大部分事情,盡管如果您只是想了解它是如何構建以提高效率的,那么值得注意的是,您正在尋找的大部分內容都在其基類BaseMachine

特別是,在BaseMachine是基於StringBuilder的東西。 LTRReplace方法中,它首先用初始字符串實例化一個 StringBuilder,從那以后的一切都純粹基於 StringBuilder。 如果我們假設內部 Microsoft .Net 實現是類似的,那么 Regex 沒有 StringBuilder 方法實際上非常煩人。

我不確定這是否對您的場景有幫助,但我遇到了一些使用 Regex 的內存消耗上限,我需要在 StringBuilder 上使用一個簡單的通配符替換擴展方法來推動它。 如果您需要復雜的正則表達式匹配和/或反向引用,這將行不通,但如果簡單 * 或 ? 通配符替換(帶有文字“替換”文本)將為您完成工作,然后我的問題末尾的解決方法至少應該給您一個提升:

有沒有人圍繞 StringBuilders 或 Streams 實現了 Regex 和/或 Xml 解析器?

這是您可以用來完成您想要的擴展方法。 它接受一個Dictionary ,其中鍵是您要查找的模式,而值是您要替換的內容。 您仍然創建傳入字符串的副本,但您只需處理一次,而不是為多次調用Regex.Replace創建副本。

public static StringBuilder BulkReplace(this StringBuilder source, IDictionary<string, string> replacementMap)
{
    if (source.Length == 0 || replacementMap.Count == 0)
    {
        return source;
    }
    string replaced = Regex.Replace(source.ToString(), String.Join("|", replacementMap.Keys.Select(Regex.Escape).ToArray()), m => replacementMap[m.Value], RegexOptions.IgnoreCase);
    return source.Clear().Append(replaced);
}

暫無
暫無

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

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