我想从字符串中删除所有特殊字符。允许输入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(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 == '.' || c == '_') {
sb.Append(c);
}
}
return sb.ToString();
}
使这种方法高效的一个原因是它具有良好的可伸缩性。执行时间将与字符串的长度相关。如果你把它用在一根大弦上,就不会有令人讨厌的意外。
编辑:
我做了一个快速的性能测试,用24个字符串运行每个函数一百万次。结果如下:
原功能:54.5毫秒。
我建议的修改:47.1毫秒。
采矿设置StringBuilder容量:43.3 ms。
正则表达式:294.4毫秒。
编辑2:
我在上面的代码中添加了A-Z和A-Z之间的区别。(我重新进行了性能测试,没有明显的差异。)
编辑3:
我测试了lookup+char[]解决方案,它运行大约13毫秒。
当然,这样做的代价是初始化庞大的查找表并将其保存在内存中。好吧,虽然没有那么多数据,但对于这样一个微不足道的函数来说,已经够多了……
private static bool[] _lookup;
static Program() {
_lookup = new bool[65536];
for (char c = '0'; c <= '9'; c++) _lookup[c] = true;
for (char c = 'A'; c <= 'Z'; c++) _lookup[c] = true;
for (char c = 'a'; c <= 'z'; c++) _lookup[c] = true;
_lookup['.'] = true;
_lookup['_'] = true;
}
public static string RemoveSpecialCharacters(string str) {
char[] buffer = new char[str.Length];
int index = 0;
foreach (char c in str) {
if (_lookup[c]) {
buffer[index] = c;
index++;
}
}
return new string(buffer, 0, index);
}
另一种试图通过减少分配来提高性能的方法,特别是在多次调用此函数的情况下。
它之所以有效,是因为可以保证结果不会比输入长,因此可以在不在内存中创建额外副本的情况下传递输入和输出。因此,您不能使用stackalloc来创建缓冲区数组,因为这需要从缓冲区中复制一个副本。
public static string RemoveSpecialCharacters(this string str)
{
return RemoveSpecialCharacters(str.AsSpan()).ToString();
}
public static ReadOnlySpan<char> RemoveSpecialCharacters(this ReadOnlySpan<char> str)
{
Span<char> buffer = new char[str.Length];
int idx = 0;
foreach (char c in str)
{
if (char.IsLetterOrDigit(c))
{
buffer[idx] = c;
idx++;
}
}
return buffer.Slice(0, idx);
}