簡體   English   中英

優化string.Replace方法

[英]Optimize string.Replace method

我列出了網站上不允許使用的200多個單詞。 下面的string.Replace方法大約需要string.Replace 如果我將s < 1000乘以10.00到s < 10,000此延遲將達到〜834ms,即增加10.43。 我擔心此功能的可伸縮性,特別是如果列表的大小增加時。 有人告訴我字符串是不可變的,並且text.Replace()在內存中創建了200個新字符串。 是否有類似於Stringbuilder東西?

List<string> FilteredWords = new List<string>();
FilteredWords.Add("RED");
FilteredWords.Add("GREEN");
FilteredWords.Add("BLACK");
for (int i = 1; i < 200; i++)
{ FilteredWords.Add("STRING " + i.ToString()); }

string text = "";

//simulate a large dynamically generated html page
for (int s = 1; s < 1000; s++)
{ text += @"Lorem ipsum dolor sit amet, minim BLACK cetero cu nam.
            No vix platonem sententiae, pro wisi congue graecis id, GREEN assum interesset in vix.
            Eum tamquam RED pertinacia ex."; }

// This is the function I seek to optimize
foreach (string s in FilteredWords)
{ text = text.Replace(s, "[REMOVED]"); }

如果您希望大多數文本比首先掃描整個文本以查找匹配的單詞相對好,那是更好的方法。 您還可以同時規范化單詞文本,以捕獲一些標准替代品。

也就是說,通過匹配單個單詞(即正則表達式,如"\\w+" )來掃描字符串,而不是替換要替換的單詞字典中的每個檢測到的單詞查找(潛在的歸一化值)。

您可以先進行掃描以獲取“要替換的單詞”列表,然后再進行替換,或者同時掃描並生成結果字符串(使用StringBuilderStreamWriter ,顯然不是String.Concat / + )。

注意:Unicode提供了大量可使用的好字符,因此不要指望您的工作會非常成功。 即,嘗試在以下文本中找到“酷”:“您是сооl”。

示例代碼(依靠Regex.Replace進行標記化並構建字符串和HashSet進行匹配)。

var toFind = FilteredWords.Aggregate(
      new HashSet<string>(), (c, i) => { c.Add(i); return c;});

text = new Regex(@"\w+")
   .Replace(text, m => toFind.Contains(m.Value) ? "[REMOVED]" : m.Value));

使用StringBuilder.Replace並嘗試將其作為批處理操作。 也就是說,您應該只創建一次StringBuilder ,因為它有一些開銷。 它不一定會快很多,但會提高內存效率。

您也應該只執行一次這種衛生處理,而不是每次請求數據時進行一次。 如果要從數據庫中讀取數據,則應考慮在將數據插入數據庫中時對其進行一次清理,因此在將其讀取並顯示到頁面上時需要做的工作較少。

也許有更好的方法,但這就是我要解決的方法。

您將需要創建一個樹結構,其中包含要替換的單詞詞典。 該類可能類似於:

public class Node 
{
    public Dictionary<char, Node> Children;
    public bool IsWord;
}

為兒童使用字典可能不是最佳選擇,但此處提供了最簡單的示例。 另外,您將需要一個構造函數來初始化Children字段。 IsWord字段用於處理已編輯的“單詞”可能是另一個已編輯的“單詞”的前綴的可能性。 例如,如果要同時刪除“紅色”和“糾正”。

您將使用每個替換單詞中的每個字符來構建樹。 例如:

public void AddWord ( string word ) 
{
    // NOTE: this assumes word is non-null and contains at least one character...

    Node currentNode = Root;

    for (int iIndex = 0; iIndex < word.Length; iIndex++)
    {
        if (currentNode.Children.ContainsKey(word[iIndex])))
        {
            currentNode = currentNode.Children[word[iIndex];
            continue;
        }

        Node newNode = new Node();
        currentNode.Children.Add(word[iIndex], newNode);
        currentNode = newNode;
    }

    // finished, mark the last node as being a complete word..
    currentNode.IsWord = true;
}

您需要在其中的某個地方處理區分大小寫的問題。 同樣,您只需要構建一次樹,之后就可以在任意數量的線程中使用它,而不必擔心鎖定,因為您只會從樹中讀取。 (基本上,我是說:將其存儲在靜態位置。)

現在,當您准備從字符串中刪除單詞時,您需要執行以下操作:

  • 創建一個StringBuilder實例來存儲結果
  • 解析源字符串,查找“單詞”的開頭和結尾。 您如何定義“單詞”將很重要。 為簡單起見,我建議從Char.IsWhitespace開始定義單詞分隔符。
  • 從樹的根部開始確定字符范圍是“單詞”后,找到與“單詞”中第一個字符關聯的子節點。
  • 如果找不到子節點,則將整個單詞添加到StringBuilder
  • 如果找到子節點,則繼續與當前節點的“子節點”匹配的下一個字符,直到字符用完或節點用完。
  • 如果到達“單詞”的末尾,請檢查最后一個節點的IsWord字段。 如果為true ,則排除該單詞,請勿將其添加到StringBuilder 如果IsWordfalse ,則不替換該單詞,而是將其添加到StringBuilder
  • 重復直到您用盡了輸入字符串。

您還需要在StringBuilder添加單詞分隔符,希望在解析輸入字符串時顯而易見。 如果您僅在輸入字符串中使用開始索引和停止索引,則應該能夠解析整個字符串而無需創建任何垃圾字符串。

完成所有這些操作后,使用StringBuilder.ToString()獲得最終結果。

您可能還需要考慮Unicode代理碼點,但你也許可以蒙混過關,而不必擔心它。

當心,我直接在此處鍵入此代碼,因此可能包括語法錯誤,錯別字和其他意外的誤導。

真正的正則表達式解決方案是:

var filteredWord = new Regex(@"\b(?:" + string.Join("|", FilteredWords.Select(Regex.Escape)) + @")\b", RegexOptions.Compiled);
text = filteredWord.Replace(text, "[REMOVED]");

我不知道這是否更快(但請注意,它也只能替換整個單詞)。

暫無
暫無

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

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