C#:从字符串中删除常见的无效字符:改进此算法

时间:2009-08-25 18:06:52

标签: c# .net algorithm

考虑从字符串中删除无效字符的要求。只需删除字符并替换为空格或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

问题: 您是否有任何关于重构此算法的建议,或任何更简单,更易于阅读,高性能,可维护的算法?

9 个答案:

答案 0 :(得分:34)

我不知道它的可读性,但正则表达式可以满足您的需要:

someString = Regex.Replace(someString, @"[!@#$%_]", "");

答案 1 :(得分:22)

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

应该做的伎俩(抱歉我手机上的语法错误很少)

答案 2 :(得分:18)

string类是不可变的(虽然是引用类型),因此它的所有静态方法都被设计为返回 new 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是坏字符的数量);如上所述,内存使用情况也好得多。

答案 3 :(得分:7)

这个 HashSet<T>快。此外,如果您必须经常执行此操作,请考虑this question I asked here的基础。

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();
}

答案 4 :(得分:4)

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

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

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

答案 5 :(得分:4)

额外提示:如果您不想记住文件无效的char数组,可以使用Path.GetInvalidFileNameChars()。如果您想要路径,那就是Path.GetInvalidPathChars

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

答案 6 :(得分:3)

为什么你真的喜欢这样做?代码绝对不简单,你只是强迫查询扩展方法进入你的代码。

另外,Contains检查在概念上和从性能角度来看似乎都是多余的。 Contains无论如何都必须遍历整个字符串,你也可以为每个字符调用Replace(bad.ToString(), string.Empty)并忘记它是否真正存在。

当然,正则表达式总是一个选项,并且在这种情况下可能更具性能(如果不是更不清楚)。

答案 7 :(得分:3)

需要考虑的事项 - 如果这是用于密码(比方说),你想要扫描并保留好字符,并假设其他一切都不好。它更容易正确过滤或好事,然后尝试猜测所有坏事。

每个角色    如果性格好 - &gt;保留它(复制到缓冲区,无论如何。)

杰夫

答案 8 :(得分:2)

这很干净。将其限制为有效字符,而不是删除无效字符。你应该把它分成常数:

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