簡體   English   中英

C#:從字符串中刪除常見的無效字符:改進此算法

[英]C#: Removing common invalid characters from a string: improve this algorithm

考慮從字符串中刪除無效字符的要求。 只需要刪除字符並替換為blank或string.Empty

char[] BAD_CHARS = new char[] { '!', '@', '#', '$', '%', '_' }; //simple example

foreach (char bad in BAD_CHARS)
{
    if (someString.Contains(bad))
      someString = someString.Replace(bad.ToString(), string.Empty);
}

真的很喜歡這樣做:

if (BAD_CHARS.Any(bc => someString.Contains(bc)))
    someString.Replace(bc,string.Empty); // bc is out of scope

問題:您對重構此算法或任何更簡單,更易於閱讀,高性能,可維護的算法有什么建議嗎?

我不知道它的可讀性,但正則表達式可以滿足您的需要:

someString = Regex.Replace(someString, @"[!@#$%_]", "");
char[] BAD_CHARS = new char[] { '!', '@', '#', '$', '%', '_' }; //simple example
someString = string.Concat(someString.Split(BAD_CHARS,StringSplitOptions.RemoveEmptyEntries));

應該做的伎倆(抱歉我的手機上有任何較小的語法錯誤)

string類是不可變的(雖然是引用類型),因此它的所有靜態方法都被設計為返回一個新的 string變量。 調用someString.Replace而不將其分配給任何東西將不會對您的程序產生任何影響。 - 好像你解決了這個問題。

您建議的算法的主要問題是它重復分配許多新的string變量,可能會導致性能大幅下降。 LINQ在這里並沒有真正的幫助。 (在我看來,我不會使代碼明顯縮短,當然也不會更具可讀性。)

請嘗試以下擴展方法。 關鍵是使用StringBuilder ,這意味着在執行期間只為結果分配了一個內存塊。

private static readonly HashSet<char> badChars = 
    new HashSet<char> { '!', '@', '#', '$', '%', '_' };

public static string CleanString(this string str)
{
    var result = new StringBuilder(str.Length);
    for (int i = 0; i < str.Length; i++)
    {
        if (!badChars.Contains(str[i]))
            result.Append(str[i]);
    }
    return result.ToString();
}

該算法還利用.NET 3.5'HashSet'類為O(1)查找檢測錯誤char的時間。 這使得整體算法O(n)而不是您發布的算法的O(nm)m是壞字符的數量); 如上所述,內存使用情況也好得多。

這個更快HashSet<T> 此外,如果您必須經常執行此操作,請考慮我在此處提出的此問題的基礎。

private static readonly bool[] BadCharValues;

static StaticConstructor()
{
    BadCharValues = new bool[char.MaxValue+1];
    char[] badChars = { '!', '@', '#', '$', '%', '_' };
    foreach (char c in badChars)
        BadCharValues[c] = true;
}

public static string CleanString(string str)
{
    var result = new StringBuilder(str.Length);
    for (int i = 0; i < str.Length; i++)
    {
        if (!BadCharValues[str[i]])
            result.Append(str[i]);
    }
    return result.ToString();
}

如果您仍想以LINQy方式執行此操作:

public static string CleanUp(this string orig)
{
    var badchars = new HashSet<char>() { '!', '@', '#', '$', '%', '_' };

    return new string(orig.Where(c => !badchars.Contains(c)).ToArray());
}

額外提示:如果您不想記住對文件無效的char數組,可以使用Path.GetInvalidFileNameChars() 如果你想要Paths,那就是Path.GetInvalidPathChars

private static string RemoveInvalidChars(string str)
            {
                return string.Concat(str.Split(Path.GetInvalidFileNameChars(), StringSplitOptions.RemoveEmptyEntries));
            }

你為什么真的喜歡這樣做? 代碼絕對不簡單,你只是強迫查詢擴展方法進入你的代碼。

另外, Contains檢查在概念上和從性能角度看都是多余的。 無論如何, Contains必須遍歷整個字符串,你也可以為每個字符調用Replace(bad.ToString(), string.Empty)並忘記它是否實際存在。

當然,正則表達式總是一種選擇,並且在這種情況下可能更具性能(如果不是更不清楚)。

需要考慮的事項 - 如果這是用於密碼(比如說),你想要掃描並保留好的角色 ,並假設其他一切都不好。 它更容易正確過濾或好事,然后嘗試猜測所有壞事。

對於每個字符如果字符是好的 - >保留它(復制到輸出緩沖區,無論如何。)

傑夫

這很干凈。 將其限制為有效字符,而不是刪除無效字符。 您可能應該將其拆分為常量:

string clean = new string(@"Sour!ce Str&*(@ing".Where(c => 
@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ,.".Contains(c)).ToArray()

暫無
暫無

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

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