[英]Regex replacements inside a StringBuilder
我正在將文本文件的內容寫入 StringBuilder,然后我想使用正則表達式對 StringBuilder 中包含的文本執行許多查找/替換操作。
我遇到了一個問題,因為 StringBuilder 替換函數無法接受正則表達式參數。
我可以在普通字符串上使用 Regex.Replace 但我的印象是這是低效的,因為需要在內存中創建字符串的兩個副本,因為 .net 字符串是不可變的。
更新文本后,我計划將其寫回原始文件。
解決我的問題的最佳和最有效的方法是什么?
編輯
除了下面的答案之外,我還發現以下問題也對我的問題有所了解 -
你的時間最好和最有效的解決辦法是先試試最簡單的方法:忘記StringBuilder
,只是使用Regex.Replace
。 然后找出它有多慢 - 它可能已經足夠好了。 不要忘記在編譯和非編譯模式下嘗試正則表達式。
如果這還不夠快,請考慮使用StringBuilder
進行任何您可以簡單表達的替換,然后使用Regex.Replace
進行其余的替換。 您可能還想考慮嘗試組合替換,減少使用的正則表達式(以及中間字符串)的數量。
您有 3 個選擇:
正如其他人在此處推薦的那樣,使用字符串以低效的方式執行此操作。
在Regex
對象上使用.Matches()
調用,並模擬.Replace()
工作方式(參見 #3)。
調整正則Regex
的 Mono 實現以構建接受StringBuilder
的正則Regex
。 幾乎所有的工作都已經在 Mono 中為您完成,但需要時間來確定使其在自己的庫中工作的部分。 奇怪的是,Mono 的Regex
Novell 2002 年的 JVM 實現Regex
。
擴展上述內容:
您可以通過調用.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 方法是唯一一種真正徹底消除字符串的方法。
這個答案自 2014 年以來一直沒有出現,我從未在評論或搜索中看到基於 StringBuilder 的正則表達式。 所以,為了讓球滾動,我從 Mono 中提取了 Regex impl 並將其放在這里:
https://github.com/brass9/RegexStringBuilder
然后,我創建了一個接口IString
以允許更松散地傳遞輸入和輸出 - string
、 StringBuilder
和char[]
每個都包裝在一個實現 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 上使用一個簡單的通配符替換擴展方法來推動它。 如果您需要復雜的正則表達式匹配和/或反向引用,這將行不通,但如果簡單 * 或 ? 通配符替換(帶有文字“替換”文本)將為您完成工作,然后我的問題末尾的解決方法至少應該給您一個提升:
這是您可以用來完成您想要的擴展方法。 它接受一個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.