我想从字符串中删除所有特殊字符。允许输入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个字符之间。


当前回答

我同意这个代码示例。唯一不同的是,我把它变成字符串类型的扩展方法。所以你可以在非常简单的一行或代码中使用它:

string test = "abc@#$123";
test.RemoveSpecialCharacters();

感谢Guffa的实验。

public static class MethodExtensionHelper
    {
    public static string RemoveSpecialCharacters(this string str)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in str)
            {
                if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
                {
                    sb.Append(c);
                }
            }
            return sb.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;
}
public static string RemoveSpecialCharacters(string str)
{
    char[] buffer = new char[str.Length];
    int idx = 0;

    foreach (char c in str)
    {
        if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
            || (c >= 'a' && c <= 'z') || (c == '.') || (c == '_'))
        {
            buffer[idx] = c;
            idx++;
        }
    }

    return new string(buffer, 0, idx);
}

最短的路只有三条线……

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

正则表达式的使用方法如下:

return Regex.Replace(strIn, @"[^\w\.@-]", "", RegexOptions.None, TimeSpan.FromSeconds(1.0));

下面的代码有以下输出(结论是,我们也可以节省一些内存资源分配数组更小的大小):

lookup = new bool[123];

for (var c = '0'; c <= '9'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'A'; c <= 'Z'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'a'; c <= 'z'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

48: 0  
49: 1  
50: 2  
51: 3  
52: 4  
53: 5  
54: 6  
55: 7  
56: 8  
57: 9  
65: A  
66: B  
67: C  
68: D  
69: E  
70: F  
71: G  
72: H  
73: I  
74: J  
75: K  
76: L  
77: M  
78: N  
79: O  
80: P  
81: Q  
82: R  
83: S  
84: T  
85: U  
86: V  
87: W  
88: X  
89: Y  
90: Z  
97: a  
98: b  
99: c  
100: d  
101: e  
102: f  
103: g  
104: h  
105: i  
106: j  
107: k  
108: l  
109: m  
110: n  
111: o  
112: p  
113: q  
114: r  
115: s  
116: t  
117: u  
118: v  
119: w  
120: x  
121: y  
122: z  

你也可以添加以下代码行来支持俄语区域设置(数组大小为1104):

for (var c = 'А'; c <= 'Я'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'а'; c <= 'я'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}