我想从字符串中删除所有特殊字符。允许输入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个字符之间。
下面的代码有以下输出(结论是,我们也可以节省一些内存资源分配数组更小的大小):
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);
}
我觉得你的算法很有效。它是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;
}
如果担心速度问题,可以使用指针编辑现有字符串。您可以固定字符串并获取指向它的指针,然后在每个字符上运行for循环,用替换字符覆盖每个无效字符。这将是非常有效的,并且不需要分配任何新的字符串内存。为了使用指针,你还需要用不安全选项来编译你的模块,并在方法头中添加“不安全”修饰符。
static void Main(string[] args)
{
string str = "string!$%with^&*invalid!!characters";
Console.WriteLine( str ); //print original string
FixMyString( str, ' ' );
Console.WriteLine( str ); //print string again to verify that it has been modified
Console.ReadLine(); //pause to leave command prompt open
}
public static unsafe void FixMyString( string str, char replacement_char )
{
fixed (char* p_str = str)
{
char* c = p_str; //temp pointer, since p_str is read-only
for (int i = 0; i < str.Length; i++, c++) //loop through each character in string, advancing the character pointer as well
if (!IsValidChar(*c)) //check whether the current character is invalid
(*c) = replacement_char; //overwrite character in existing string with replacement character
}
}
public static bool IsValidChar( char c )
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '.' || c == '_');
//return char.IsLetterOrDigit( c ) || c == '.' || c == '_'; //this may work as well
}