27

我正在使用这种方法来清理字符串

public static string CleanString(string dirtyString)
{
    string removeChars = " ?&^$#@!()+-,:;<>’\'-_*";
    string result = dirtyString;

    foreach (char c in removeChars)
    {
        result = result.Replace(c.ToString(), string.Empty);
    }

    return result;
}

这种方法效果很好。但是这种方法存在性能故障。每次我传递字符串时,每个字符都会进入循环,如果我有一个大字符串,那么返回对象将花费太多时间。

有没有其他更好的方法来做同样的事情?就像在 LINQ 或 JQUERY / Javascript 中一样

任何建议将不胜感激。

4

9 回答 9

48

好的,考虑以下测试:

public class CleanString
{
    //by MSDN http://msdn.microsoft.com/en-us/library/844skk0h(v=vs.71).aspx
    public static string UseRegex(string strIn)
    {
        // Replace invalid characters with empty strings.
        return Regex.Replace(strIn, @"[^\w\.@-]", "");
    }

    // by Paolo Tedesco
    public static String UseStringBuilder(string strIn)
    {
        const string removeChars = " ?&^$#@!()+-,:;<>’\'-_*";
        // specify capacity of StringBuilder to avoid resizing
        StringBuilder sb = new StringBuilder(strIn.Length);
        foreach (char x in strIn.Where(c => !removeChars.Contains(c)))
        {
            sb.Append(x);
        }
        return sb.ToString();
    }

    // by Paolo Tedesco, but using a HashSet
    public static String UseStringBuilderWithHashSet(string strIn)
    {
        var hashSet = new HashSet<char>(" ?&^$#@!()+-,:;<>’\'-_*");
        // specify capacity of StringBuilder to avoid resizing
        StringBuilder sb = new StringBuilder(strIn.Length);
        foreach (char x in strIn.Where(c => !hashSet.Contains(c)))
        {
            sb.Append(x);
        }
        return sb.ToString();
    }

    // by SteveDog
    public static string UseStringBuilderWithHashSet2(string dirtyString)
    {
        HashSet<char> removeChars = new HashSet<char>(" ?&^$#@!()+-,:;<>’\'-_*");
        StringBuilder result = new StringBuilder(dirtyString.Length);
        foreach (char c in dirtyString)
            if (removeChars.Contains(c))
                result.Append(c);
        return result.ToString();
    }

    // original by patel.milanb
    public static string UseReplace(string dirtyString)
    {
        string removeChars = " ?&^$#@!()+-,:;<>’\'-_*";
        string result = dirtyString;

        foreach (char c in removeChars)
        {
            result = result.Replace(c.ToString(), string.Empty);
        }

        return result;
    }

    // by L.B
    public static string UseWhere(string dirtyString)
    {
        return new String(dirtyString.Where(Char.IsLetterOrDigit).ToArray());
    }
}

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        var dirtyString = "sdfdf.dsf8908()=(=(sadfJJLef@ssyd€sdöf////fj()=/§(§&/(\"&sdfdf.dsf8908()=(=(sadfJJLef@ssyd€sdöf////fj()=/§(§&/(\"&sdfdf.dsf8908()=(=(sadfJJLef@ssyd€sdöf";
        var sw = new Stopwatch();

        var iterations = 50000;

        sw.Start();
        for (var i = 0; i < iterations; i++)
            CleanString.<SomeMethod>(dirtyString);
        sw.Stop();
        Debug.WriteLine("CleanString.<SomeMethod>: " + sw.ElapsedMilliseconds.ToString());
        sw.Reset();

        ....
        <repeat>
        ....       
    }
}

输出

CleanString.UseReplace: 791
CleanString.UseStringBuilder: 2805
CleanString.UseStringBuilderWithHashSet: 521
CleanString.UseStringBuilderWithHashSet2: 331
CleanString.UseRegex: 1700
CleanString.UseWhere: 233

结论

您使用哪种方法可能并不重要。

连续调用 50000(!) 次时,禁食 ( UseWhere: 233ms) 和最慢 ( UseStringBuilder: 2805ms) 方法之间的时间差为 2572ms。如果不经常运行该方法,您可能不需要关心它。

但是如果你这样做,使用UseWhere方法(由LB编写);但也请注意,它略有不同。

于 2012-07-09T13:29:48.343 回答
7

如果您追求的是纯粹的速度和效率,我建议您这样做:

public static string CleanString(string dirtyString)
{
    HashSet<char> removeChars = new HashSet<char>(" ?&^$#@!()+-,:;<>’\'-_*");
    StringBuilder result = new StringBuilder(dirtyString.Length);
    foreach (char c in dirtyString)
        if (!removeChars.Contains(c)) // prevent dirty chars
            result.Append(c);
    return result.ToString();
}

RegEx 无疑是一个优雅的解决方案,但它增加了额外的开销。通过指定字符串构建器的起始长度,它只需要分配一次内存(ToString最后是第二次)。这将减少内存使用并提高速度,尤其是在较长的字符串上。

但是,正如 LB 所说,如果您使用它来正确编码绑定到 HTML 输出的文本,您应该使用HttpUtility.HtmlEncode它而不是自己做。

于 2012-07-09T13:29:47.260 回答
4

使用正则表达式[?&^$#@!()+-,:;<>’\'-_*]替换为空字符串

于 2012-07-09T13:16:17.950 回答
2

我不知道在性能方面,使用 aRegex或 LINQ 是否会有所改进。
可能有用的东西是使用 aStringBuilder而不是string.Replace每次使用来创建新字符串:

using System.Linq;
using System.Text;

static class Program {
    static void Main(string[] args) {
        const string removeChars = " ?&^$#@!()+-,:;<>’\'-_*";
        string result = "x&y(z)";
        // specify capacity of StringBuilder to avoid resizing
        StringBuilder sb = new StringBuilder(result.Length);
        foreach (char x in result.Where(c => !removeChars.Contains(c))) {
            sb.Append(x);
        }
        result = sb.ToString();
    }
}
于 2012-07-09T13:30:01.787 回答
2

这个速度更快!
采用:

string dirty=@"tfgtf$@$%gttg%$% 664%$";
string clean = dirty.Clean();


    public static string Clean(this String name)
    {
        var namearray = new Char[name.Length];

        var newIndex = 0;
        for (var index = 0; index < namearray.Length; index++)
        {
            var letter = (Int32)name[index];

            if (!((letter > 96 && letter < 123) || (letter > 64 && letter < 91) || (letter > 47 && letter < 58)))
                continue;

            namearray[newIndex] = (Char)letter;
            ++newIndex;
        }

        return new String(namearray).TrimEnd();
    }
于 2014-03-19T10:03:13.107 回答
1

试试这个:http: //msdn.microsoft.com/en-us/library/xwewhkd1.aspx

于 2012-07-09T13:17:19.057 回答
1

也许先解释“为什么”然后再解释“是什么”会有所帮助。性能下降的原因是因为 c# 会复制并替换每次替换的字符串。根据我在 .NET 中使用 Regex 的经验,它并不总是更好 - 尽管在大多数情况下(我认为包括这个)它可能会工作得很好。

如果我真的需要性能,我通常不会让它靠运气,而只是告诉编译器我想要什么:即:创建一个具有上限字符数的字符串,然后复制你需要的所有字符。也可以用开关/案例或数组替换散列集,在这种情况下,您最终可能会得到一个跳转表或数组查找——这甚至更快。

“务实”的最佳但快速的解决方案是:

char[] data = new char[dirtyString.Length];
int ptr = 0;
HashSet<char> hs = new HashSet<char>() { /* all your excluded chars go here */ };
foreach (char c in dirtyString)
    if (!hs.Contains(c))
        data[ptr++] = c;
return new string(data, 0, ptr);

顺便说一句:当您想要处理高代理 Unicode 字符时,此解决方案不正确 - 但可以轻松调整以包含这些字符。

-斯特凡。

于 2012-07-09T13:51:30.397 回答
1

我在我当前的项目中使用它,它工作正常。它需要一个句子,它删除所有非字母数字字符,然后返回包含第一个字母大写的所有单词和小写其他所有单词的句子。也许我应该称它为 SentenceNormalizer。命名很难:)

    internal static string StringSanitizer(string whateverString)
{
    whateverString = whateverString.Trim().ToLower();
    Regex cleaner = new Regex("(?:[^a-zA-Z0-9 ])", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
    var listOfWords = (cleaner.Replace(whateverString, string.Empty).Split(' ', StringSplitOptions.RemoveEmptyEntries)).ToList();
    string cleanString = string.Empty;
    foreach (string word in listOfWords)
    {
        cleanString += $"{word.First().ToString().ToUpper() + word.Substring(1)} ";
    }
    return cleanString;
}
于 2019-08-21T01:32:59.070 回答
0

我无法花时间对此进行酸性测试,但这条线实际上并没有按照需要清理斜线。

HashSet<char> removeChars = new HashSet<char>(" ?&^$#@!()+-,:;<>’\'-_*");

我必须单独添加斜杠并转义反斜杠

HashSet<char> removeChars = new HashSet<char>(" ?&^$#@!()+-,:;<>’'-_*");
removeChars.Add('/');
removeChars.Add('\\');
于 2015-01-21T20:05:41.793 回答