我想从字符串中删除所有特殊字符。允许输入A-Z(大写或小写)、数字(0-9)、下划线(_)或点符号(.)。

我有以下,它是有效的,但我怀疑(我知道!)它不是很有效:

    public static string RemoveSpecialCharacters(string str)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.Length; i++)
        {
            if ((str[i] >= '0' && str[i] <= '9')
                || (str[i] >= 'A' && str[i] <= 'z'
                    || (str[i] == '.' || str[i] == '_')))
                {
                    sb.Append(str[i]);
                }
        }

        return sb.ToString();
    }

最有效的方法是什么?正则表达式是什么样子的,它与普通字符串操作相比如何?

要清洗的字符串相当短,长度通常在10到30个字符之间。


当前回答

最短的路只有三条线……

public static string RemoveSpecialCharacters(string str)
{
    var sb = new StringBuilder();
    foreach (var c in str.Where(c => c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '.' || c == '_')) sb.Append(c); 
    return sb.ToString();
}

其他回答

我建议创建一个简单的查找表,您可以在静态构造函数中初始化它,以将任何字符组合设置为有效。这让您可以进行快速、单一的检查。

edit

另外,为了提高速度,您需要将StringBuilder的容量初始化为输入字符串的长度。这将避免重新分配。这两种方法结合起来会给你速度和灵活性。

另一个编辑

我认为编译器可能会优化它,但作为风格和效率的问题,我建议foreach而不是for。

Use:

s.erase(std::remove_if(s.begin(), s.end(), my_predicate), s.end());

bool my_predicate(char c)
{
 return !(isalpha(c) || c=='_' || c==' '); // depending on you definition of special characters
}

你会得到一个干净的字符串s。

Erase()将去掉所有特殊字符,并且可以使用my_predicate()函数进行高度自定义。

I had to do something similar for work, but in my case I had to filter all that is not a letter, number or whitespace (but you could easily modify it to your needs). The filtering is done client-side in JavaScript, but for security reasons I am also doing the filtering server-side. Since I can expect most of the strings to be clean, I would like to avoid copying the string unless I really need to. This let my to the implementation below, which should perform better for both clean and dirty strings.

public static string EnsureOnlyLetterDigitOrWhiteSpace(string input)
{
    StringBuilder cleanedInput = null;
    for (var i = 0; i < input.Length; ++i)
    {
        var currentChar = input[i];
        var charIsValid = char.IsLetterOrDigit(currentChar) || char.IsWhiteSpace(currentChar);

        if (charIsValid)
        {
            if(cleanedInput != null)
                cleanedInput.Append(currentChar);
        }
        else
        {
            if (cleanedInput != null) continue;
            cleanedInput = new StringBuilder();
            if (i > 0)
                cleanedInput.Append(input.Substring(0, i));
        }
    }

    return cleanedInput == null ? input : cleanedInput.ToString();
}

我觉得你的算法很有效。它是O(n),并且只查看每个字符一次。除非你在检查它们之前神奇地知道它们的值,否则你不会比这更好。

但是,我将把StringBuilder的容量初始化为字符串的初始大小。我猜您认为的性能问题来自内存重新分配。

旁注:检查A-z并不安全。你要包括[,\,],^,_和'…

旁注2:为了获得额外的效率,将比较按顺序排列,以减少比较的数量。(在最坏的情况下,你说的是8个比较,所以不要想太多。)这随你的预期输入而变化,但一个例子可以是:

if (str[i] >= '0' && str[i] <= 'z' && 
    (str[i] >= 'a' || str[i] <= '9' ||  (str[i] >= 'A' && str[i] <= 'Z') || 
    str[i] == '_') || str[i] == '.')

旁注3:如果出于某种原因,你真的需要它更快,那么switch语句可能更快。编译器应该为你创建一个跳转表,结果只有一个比较:

switch (str[i])
{
    case '0':
    case '1':
    .
    .
    .
    case '.':
        sb.Append(str[i]);
        break;
}

我将使用字符串替换为正则表达式搜索“特殊字符”,替换所有字符找到一个空字符串。