我想从字符串中删除所有特殊字符.允许的字符是AZ(大写或小写),数字(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();
}
Run Code Online (Sandbox Code Playgroud)
最有效的方法是什么?正则表达式会是什么样的,它与正常的字符串操作相比如何?
将要清理的字符串相当短,通常长度在10到30个字符之间.
Guf*_*ffa 313
为什么你认为你的方法效率不高?它实际上是您可以做到的最有效的方法之一.
您当然应该将字符读入局部变量或使用枚举器来减少数组访问次数:
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();
}
Run Code Online (Sandbox Code Playgroud)
使这种方法有效的一点是它可以很好地扩展.执行时间将相对于字符串的长度.如果你想在一个大字符串上使用它,没有令人讨厌的惊喜.
编辑:
我做了一个快速的性能测试,用24个字符串运行每个函数一百万次.这些是结果:
原始功能:54.5毫秒.
我建议的改变:47.1 ms.
设置StringBuilder容量为43.3毫秒.
正则表达式:294.4 ms.
编辑2:我在上面的代码中添加了AZ和az之间的区别.(我重新进行了性能测试,并没有明显的区别.)
编辑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);
}
Run Code Online (Sandbox Code Playgroud)
Bli*_*ixt 183
好吧,除非你真的需要从你的功能中挤出性能,否则就去使用最容易维护和理解的东西.正则表达式如下所示:
为了获得额外的性能,您可以预先编译它,也可以告诉它在第一次调用时编译(后续调用会更快.)
public static string RemoveSpecialCharacters(string str)
{
return Regex.Replace(str, "[^a-zA-Z0-9_.]+", "", RegexOptions.Compiled);
}
Run Code Online (Sandbox Code Playgroud)
Ste*_*dit 15
我建议创建一个简单的查找表,您可以在静态构造函数中初始化,以将任何字符组合设置为有效.这使您可以快速进行单一检查.
编辑
此外,为了提高速度,您需要将StringBuilder的容量初始化为输入字符串的长度.这将避免重新分配.这两种方法将为您提供速度和灵活性.
另一个编辑
我认为编译器可能会优化它,但作为风格和效率的问题,我推荐foreach而不是for.
Luk*_*keH 12
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);
}
Run Code Online (Sandbox Code Playgroud)
CMS*_*CMS 11
正则表达式如下所示:
public string RemoveSpecialChars(string input)
{
return Regex.Replace(input, @"[^0-9a-zA-Z\._]", string.Empty);
}
Run Code Online (Sandbox Code Playgroud)
但是如果性能非常重要,我建议你在选择"正则表达式路径"之前先做一些基准测试......
Sha*_*ser 10
如果您使用的是动态字符列表,LINQ可能会提供更快更优雅的解决方案:
public static string RemoveSpecialCharacters(string value, char[] specialCharacters)
{
return new String(value.Except(specialCharacters).ToArray());
}
Run Code Online (Sandbox Code Playgroud)
我将这种方法与之前的两种"快速"方法(发布编译)进行了比较:
请注意,该算法稍作修改 - 字符作为数组而不是硬编码传入,这可能会略微影响事物(即/其他解决方案将有一个内部foor循环来检查字符数组).
如果我使用LINQ where子句切换到硬编码解决方案,结果是:
如果您计划编写更通用的解决方案,而不是对字符列表进行硬编码,那么可能值得查看LINQ或修改后的方法.LINQ绝对能为您提供简洁,高度可读的代码 - 甚至比Regex还要强大.
我不相信你的算法不算高效.它是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] == '.')
Run Code Online (Sandbox Code Playgroud)
旁注3:如果出于某种原因你真的需要快速,那么switch语句可能会更快.编译器应该为您创建一个跳转表,结果只有一个比较:
switch (str[i])
{
case '0':
case '1':
.
.
.
case '.':
sb.Append(str[i]);
break;
}
Run Code Online (Sandbox Code Playgroud)
您可以使用正则表达式如下:
return Regex.Replace(strIn, @"[^\w\.@-]", "", RegexOptions.None, TimeSpan.FromSeconds(1.0));
Run Code Online (Sandbox Code Playgroud)