从字符串中删除特殊字符的最有效方法

316

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


5
我不会把这些内容写在答案里,因为这样并没有更高效的作用。但是,你可以使用像char.IsLetterOrDigit()这样的静态字符方法在if语句中,使它更易读。 - Martin Harris
5
我不确定检查 A 到 Z 是否安全,因为它会包含 6 个非字母的字符,其中只有一个是所需的(下划线)。 - Steven Sudit
4
专注于使您的代码更易读。除非您要在循环中执行此操作,例如每秒500次,否则效率并不是一个大问题。使用正则表达式,这将使阅读变得更加容易。 - Byron Whitlock
5
拜伦,你关于强调易读性的想法可能是正确的。然而,我对正则表达式能否易读持怀疑态度。 :-) - Steven Sudit
2
正则表达式是否易读有点像德语是否易读;这取决于你是否了解它(尽管在两种情况下,你偶尔会遇到毫无意义的语法规则 ;))。 - Blixt
显示剩余3条评论
28个回答

1

我在想基于正则表达式的替换(可能已编译)是否更快。 需要测试一下 有人发现这个要慢大约五倍。

除此之外,建议你初始化 StringBuilder 的长度,这样中间字符串在增长时就不必被复制。

一个好的长度是原始字符串的长度,或者略小一些(取决于函数输入的性质)。

最后,你可以使用查找表(在0..127范围内)来确定一个字符是否可接受。


一个正则表达式已经被测试过了,它大约慢了五倍。使用0..127范围内的查找表时,仍然需要在使用查找表之前对字符代码进行范围检查,因为字符是16位值而不是7位值。 - Guffa
@Guffa 嗯...是的? ;) - Christian Klauser

0

最短的方法只需要3行代码...

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

0

我不确定这是最有效的方法,但它对我有用

 Public Function RemoverTildes(stIn As String) As String
    Dim stFormD As String = stIn.Normalize(NormalizationForm.FormD)
    Dim sb As New StringBuilder()

    For ich As Integer = 0 To stFormD.Length - 1
        Dim uc As UnicodeCategory = CharUnicodeInfo.GetUnicodeCategory(stFormD(ich))
        If uc <> UnicodeCategory.NonSpacingMark Then
            sb.Append(stFormD(ich))
        End If
    Next
    Return (sb.ToString().Normalize(NormalizationForm.FormC))
End Function

答案确实可行,但问题是关于 C# 的。(顺便说一句:我知道这实际上是五年前的事了,但还是..)我使用了Telerik VB转换为C#,(反之亦然),代码工作得非常好 - 不确定其他人是否也这样。 (另外一个问题,https://converter.telerik.com/) - Momoro

0

如果您需要在注入或打字错误(罕见事件)的情况下清理输入字符串,则最快的方法是使用switch()检查所有字符(编译器可以很好地优化switch()的执行时间),并添加代码以删除找到的不需要的字符。以下是解决方案:

    public static string RemoveExtraCharacters(string input)
    {
        if (string.IsNullOrEmpty(input))
            return "";

        input = input.Trim();

        StringBuilder sb = null;

    reStart:
        if (!string.IsNullOrEmpty(input))
        {
            var len = input.Length; ;

            for (int i = 0; i < len; i++)
            {
                switch (input[i])
                {
                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                    case 'A':
                    case 'B':
                    case 'C':
                    case 'D':
                    case 'E':
                    case 'F':
                    case 'G':
                    case 'H':
                    case 'I':
                    case 'J':
                    case 'K':
                    case 'L':
                    case 'M':
                    case 'N':
                    case 'O':
                    case 'Q':
                    case 'P':
                    case 'R':
                    case 'S':
                    case 'T':
                    case 'U':
                    case 'V':
                    case 'W':
                    case 'X':
                    case 'Y':
                    case 'Z':
                    case 'a':
                    case 'b':
                    case 'c':
                    case 'd':
                    case 'e':
                    case 'f':
                    case 'g':
                    case 'h':
                    case 'i':
                    case 'j':
                    case 'k':
                    case 'l':
                    case 'm':
                    case 'n':
                    case 'o':
                    case 'q':
                    case 'p':
                    case 'r':
                    case 's':
                    case 't':
                    case 'u':
                    case 'v':
                    case 'w':
                    case 'x':
                    case 'y':
                    case 'z':
                    case '/':
                    case '_':
                    case '-':
                    case '+':
                    case '.':
                    case ',':
                    case '*':
                    case ':':
                    case '=':
                    case ' ':
                    case '^':
                    case '$':
                        break;  

                    default:
                        if (sb == null)
                            sb = new StringBuilder();

                        sb.Append(input.Substring(0, i));
                        if (i + 1 < len)
                        {
                            input = input.Substring(i + 1);
                            goto reStart;
                        }
                        else
                            input = null;
                        break;
                }
            }
        }

        if (sb != null)
        {
            if (input != null)
                sb.Append(input);
            return sb.ToString();
        }

        return input;
    }

-1
public static string RemoveAllSpecialCharacters(this string text) {
  if (string.IsNullOrEmpty(text))
    return text;

  string result = Regex.Replace(text, "[:!@#$%^&*()}{|\":?><\\[\\]\\;'/.,~]", " ");
  return result;
}

答案是错误的。如果你要使用正则表达式,它应该是包容性的,而不是排除性的,因为现在你错过了一些字符。实际上,已经有一个使用正则表达式的答案了。而且为了完整起见,正则表达式比直接比较字符的函数更慢。 - TPAKTOPA

-2

使用 LINQ 的简单方法

string text = "123a22 ";
var newText = String.Join(string.Empty, text.Where(x => x != 'a'));

-3
如果您担心速度问题,可以使用指针来编辑现有的字符串。您可以将字符串固定并获取指向它的指针,然后在每个字符上运行for循环,用替换字符覆盖每个无效字符。这将非常高效,并且不需要分配任何新的字符串内存。您还需要使用不安全选项编译您的模块,并在方法头中添加“unsafe”修饰符才能使用指针。
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
}

15
不!在.NET中更改字符串是不可取的!整个框架都依赖于字符串是不可变的规则,如果打破这个规则,可能会产生意想不到的副作用... - Guffa

-3
public static string RemoveSpecialCharacters(string str){
    return str.replaceAll("[^A-Za-z0-9_\\\\.]", "");
}

1
很抱歉,replaceAll不是C#字符串函数,而是Java或JavaScript的函数。 - Csaba Toth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接