【问题标题】:Most efficient way to remove special characters from string从字符串中删除特殊字符的最有效方法
【发布时间】:2010-11-10 08:23:38
【问题描述】:

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

【问题讨论】:

  • 我不会把它放在答案中,因为它不会更有效,但是有许多静态 char 方法,如 char.IsLetterOrDigit() 可以在 if 语句中使用至少让它更清晰。
  • 我不确定检查 A 到 z 是否安全,因为它会输入 6 个非字母字符,只需要其中一个字符(下划线)。
  • 专注于使您的代码更具可读性。除非您以每秒 500 次的循环执行此操作,否则效率并不是什么大问题。使用正则表达式,它会更容易阅读。l
  • 拜伦,您需要强调可读性可能是对的。但是,我对正则表达式的可读性持怀疑态度。 :-)
  • 正则表达式是否可读有点像德语是否可读;这取决于您是否知道(尽管在这两种情况下,您都会不时遇到毫无意义的语法规则;)

标签: c# string


【解决方案1】:

为什么你认为你的方法效率不高?这实际上是您可以做到的最有效的方法之一。

您当然应该将字符读入局部变量或使用枚举器来减少数组访问次数:

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 毫秒。
正则表达式: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);
}

【讨论】:

  • 我同意。我要做的唯一其他更改是将初始容量参数添加到 StringBuilder 构造函数“= new StringBuilder(str.Length)”。
  • 根据我的测试,我的答案是使用char[] 缓冲区而不是StringBuilder,在这个问题上略有优势。 (虽然我的可读性较差,所以性能上的小小好处可能不值得。)
  • @Steven:很可能是这样,但基准不言自明!在我的测试中,使用char[] 缓冲区的性能(略)优于StringBuilder,即使在扩展到数万个字符长度的字符串时也是如此。
  • @downvoter:为什么要投反对票?如果你不解释你认为错误的地方,它就无法改进答案。
  • @SILENT:不,它没有,但你应该只这样做一次。如果每次调用该方法时分配一个那么大的数组(并且如果您频繁调用该方法),那么该方法将成为迄今为止最慢的方法,并为垃圾收集器带来大量工作。
【解决方案2】:

好吧,除非你真的需要从你的函数中挤出性能,否则就选择最容易维护和理解的东西。正则表达式如下所示:

为了提高性能,您可以预编译它,也可以告诉它在第一次调用时编译(后续调用会更快。)

public static string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_.]+", "", RegexOptions.Compiled);
}

【讨论】:

  • 我猜这可能是一个足够复杂的查询,它会比 OP 的方法更快,尤其是在预编译的情况下。然而,我没有证据支持这一点。应该对其进行测试。除非它非常慢,否则无论如何我都会选择这种方法,因为它更容易阅读和维护。 +1
  • 它是一个非常简单的正则表达式(没有回溯或任何复杂的东西),所以它应该非常快。
  • @rmeador:如果不编译,它的速度大约慢 5 倍,编译速度比他的方法慢 3 倍。虽然仍然简单 10 倍:-D
  • 正则表达式不是神奇的锤子,也永远不会比手工优化的代码快。
  • 对于那些记得 Knuth 关于优化的名言的人,这就是开始的地方。然后,如果您发现需要额外的千分之一毫秒的性能,请使用其他技术之一。
【解决方案3】:

我建议创建一个简单的查找表,您可以在静态构造函数中对其进行初始化,以将任意字符组合设置为有效。这让您可以进行快速、单一的检查。

编辑

另外,为了速度,您需要将 StringBuilder 的容量初始化为输入字符串的长度。这将避免重新分配。将这两种方法结合使用将为您提供速度和灵活性。

另一个编辑

我认为编译器可能会对其进行优化,但考虑到样式和效率,我建议使用 foreach 而不是 for。

【讨论】:

  • 对于数组,forforeach 产生类似的代码。我不知道字符串虽然。我怀疑 JIT 是否知道 String 的类数组性质。
  • 我敢打赌,JIT 比您的 [笑话删除] 更了解字符串的类似数组的性质。 Anders etal 做了很多工作来优化 .net 中关于字符串的所有内容
  • 我已经使用 HashSet 完成了这个,它比他的方法慢了大约 2 倍。使用 bool[] 比他在 OP 中的版本快一点(0.0469ms/iter v. 0.0559ms/iter)......但可读性较差。
  • 我看不出使用 bool 数组和 int 数组之间的任何性能差异。我会使用 bool 数组,因为它会将查找表从 256 kb 降低到 64 kb,但对于这样一个微不足道的函数来说,它仍然需要大量数据......而且它只快 30% 左右。
  • @Guffa 2) 鉴于我们只保留字母数字和一些基本拉丁字符,我们只需要一个用于低字节的表,因此大小并不是真正的问题。如果我们想要通用,那么标准的 Unicode 技术就是双重间接。换句话说,一个包含 256 个表引用的表,其中许多都指向同一个空表。
【解决方案4】:
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);
}

【讨论】:

  • +1,经过测试,比 StringBuilder 快 40%。 0.0294 毫秒/字符串与 0.0399 毫秒/字符串
  • 只是为了确定,你的意思是有或没有预分配的StringBuilder?
  • 使用预分配,仍然比 char[] 分配和新字符串慢 40%。
  • 我喜欢这个。我调整了这个方法foreach (char c in input.Where(c =&gt; char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x =&gt; x == c))) buffer[idx++] = c;
【解决方案5】:

正则表达式如下所示:

public string RemoveSpecialChars(string input)
{
    return Regex.Replace(input, @"[^0-9a-zA-Z\._]", string.Empty);
}

但如果性能非常重要,我建议您在选择“正则表达式路径”之前做一些基准测试...

【讨论】:

    【解决方案6】:

    如果您使用的是动态字符列表,LINQ 可能会提供更快、更优雅的解决方案:

    public static string RemoveSpecialCharacters(string value, char[] specialCharacters)
    {
        return new String(value.Except(specialCharacters).ToArray());
    }
    

    我将这种方法与之前的两种“快速”方法(发布编译)进行了比较:

    • LukeH 的字符数组解决方案 - 427 毫秒
    • StringBuilder 解决方案 - 429 毫秒
    • LINQ(此答案)- 98 毫秒

    请注意,该算法稍作修改 - 字符作为数组而不是硬编码传入,这可能会稍微影响一些事情(即/其他解决方案将有一个内部 foror 循环来检查字符数组)。

    如果我使用 LINQ where 子句切换到硬编码解决方案,结果是:

    • 字符数组解决方案 - 7 毫秒
    • StringBuilder 解决方案 - 22 毫秒
    • LINQ - 60 毫秒

    如果您打算编写更通用的解决方案,而不是对字符列表进行硬编码,那么可能值得研究 LINQ 或修改后的方法。 LINQ 绝对可以为您提供简洁、易读的代码——甚至比正则表达式还要多。

    【讨论】:

    • 这种方法看起来不错,但它不起作用 - except() 是一个集合操作,因此您最终只会得到字符串中每个唯一字符的第一次出现。
    【解决方案7】:

    我不相信你的算法是高效的。这是 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;
    }
    

    【讨论】:

    • 我同意你不能在这个上击败 O(n)。但是,每次比较的成本可以降低。表查找具有较低的固定成本,而随着您添加更多异常,一系列比较的成本将会增加。
    • 关于旁注3,你真的认为跳转表会比查表快吗?
    • 我对交换机方案进行了快速性能测试,结果与对比结果相同。
    • @Steven Sudit - 我敢说他们实际上差不多。想进行测试吗?
    • O(n) 符号有时让我很生气。人们会基于算法已经是 O(n) 的事实做出愚蠢的假设。如果我们更改此例程以将 str[i] 调用替换为一个函数,该函数通过与世界另一端的服务器构建一次性 SSL 连接来检索比较值......你肯定会看到巨大的性能差异,算法仍然是 O(n)。每种算法的 O(1) 成本都很重要,而且不等价!
    【解决方案8】:

    你可以使用正则表达式如下:

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

    【讨论】:

      【解决方案9】:
      StringBuilder sb = new StringBuilder();
      
      for (int i = 0; i < fName.Length; i++)
      {
         if (char.IsLetterOrDigit(fName[i]))
          {
             sb.Append(fName[i]);
          }
      }
      

      【讨论】:

        【解决方案10】:

        这对我来说似乎很好。我要做的唯一改进是用字符串的长度初始化StringBuilder

        StringBuilder sb = new StringBuilder(str.Length);
        

        【讨论】:

          【解决方案11】:

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

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

          【讨论】:

            【解决方案12】:

            我会使用字符串替换和正则表达式搜索“特殊字符”,将找到的所有字符替换为空字符串。

            【讨论】:

            • +1 代码肯定更少,并且可以说更易读,忽略一次写入正则表达式。
            • @kenny - 我同意。最初的问题甚至指出字符串很短 - 10-30 个字符。但显然很多人仍然认为我们正在以秒为单位出售 CPU 时间......
            • Reguler expressin 太懒惰了,所以不要一直用。
            【解决方案13】:

            我不得不为工作做一些类似的事情,但在我的情况下,我必须过滤所有不是字母、数字或空格的东西(但您可以根据需要轻松修改它)。 过滤是在 JavaScript 中的客户端完成的,但出于安全原因,我也在服务器端进行过滤。由于我可以预期大多数字符串都是干净的,因此除非我真的需要,否则我想避免复制字符串。这让我可以实现下面的实现,它应该对干净和脏字符串都有更好的表现。

            public static string EnsureOnlyLetterDigitOrWhiteSpace(string input)
            {
                StringBuilder cleanedInput = null;
                for (var i = 0; i < input.Length; ++i)
                {
                    var currentChar = input[i];
                    var charIsValid = char.IsLetterOrDigit(currentChar) || char.IsWhiteSpace(currentChar);
            
                    if (charIsValid)
                    {
                        if(cleanedInput != null)
                            cleanedInput.Append(currentChar);
                    }
                    else
                    {
                        if (cleanedInput != null) continue;
                        cleanedInput = new StringBuilder();
                        if (i > 0)
                            cleanedInput.Append(input.Substring(0, i));
                    }
                }
            
                return cleanedInput == null ? input : cleanedInput.ToString();
            }
            

            【讨论】:

              【解决方案14】:

              这里有很多建议的解决方案,有些比其他的更有效,但可能不是很可读。这可能不是最有效的,但肯定适用于大多数情况,并且非常简洁易读,利用了 Linq:

              string stringToclean = "This is a test.  Do not try this at home; you might get hurt. Don't believe it?";
              
              var validPunctuation = new HashSet<char>(". -");
              
              var cleanedVersion = new String(stringToclean.Where(x => (x >= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z') || validPunctuation.Contains(x)).ToArray());
              
              var cleanedLowercaseVersion = new String(stringToclean.ToLower().Where(x => (x >= 'a' && x <= 'z') || validPunctuation.Contains(x)).ToArray());
              

              【讨论】:

                【解决方案15】:

                对于 S&G 的,Linq-ified 方式:

                var original = "(*^%foo)(@)&^@#><>?:\":';=-+_";
                var valid = new char[] { 
                    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 
                    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 
                    'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 
                    'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', 
                    '9', '0', '.', '_' };
                var result = string.Join("",
                    (from x in original.ToCharArray() 
                     where valid.Contains(x) select x.ToString())
                        .ToArray());
                

                不过,我认为这不会是最有效的方法。

                【讨论】:

                • 不是,因为它是线性搜索。
                【解决方案16】:
                public string RemoveSpecial(string evalstr)
                {
                StringBuilder finalstr = new StringBuilder();
                            foreach(char c in evalstr){
                            int charassci = Convert.ToInt16(c);
                            if (!(charassci >= 33 && charassci <= 47))// special char ???
                             finalstr.append(c);
                            }
                return finalstr.ToString();
                }
                

                【讨论】:

                  【解决方案17】:

                  用途:

                  s.erase(std::remove_if(s.begin(), s.end(), my_predicate), s.end());
                  
                  bool my_predicate(char c)
                  {
                   return !(isalpha(c) || c=='_' || c==' '); // depending on you definition of special characters
                  }
                  

                  你会得到一个干净的字符串s

                  erase() 将去除所有特殊字符,并通过 my_predicate() 函数高度自定义。

                  【讨论】:

                    【解决方案18】:

                    HashSet 是 O(1)
                    不知道是不是比现有的比较快

                    private static HashSet<char> ValidChars = new HashSet<char>() { 'a', 'b', 'c', 'A', 'B', 'C', '1', '2', '3', '_' };
                    public static string RemoveSpecialCharacters(string str)
                    {
                        StringBuilder sb = new StringBuilder(str.Length / 2);
                        foreach (char c in str)
                        {
                            if (ValidChars.Contains(c)) sb.Append(c);
                        }
                        return sb.ToString();
                    }
                    

                    我进行了测试,这并不比接受的答案快。
                    我会保留它,就好像您需要一组可配置的字符一样,这​​将是一个很好的解决方案。

                    【讨论】:

                    • 为什么你认为比较不是O(1)?
                    • @Guffa 我不确定不是,我删除了我的评论。和+1。在发表评论之前我应该​​做更多的测试。
                    【解决方案19】:

                    我想知道基于正则表达式的替换(可能已编译)是否更快。 必须测试一下有人发现这要慢约 5 倍。

                    除此之外,您应该使用预期长度初始化 StringBuilder,这样中间字符串在增长时不必被复制。

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

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

                    【讨论】:

                    • 一个正则表达式已经测试过了,它慢了大约五倍。对于 0..127 范围内的查找表,您仍然需要在使用查找表之前对字符代码进行范围检查,因为字符是 16 位值,而不是 7 位值。
                    • @Guffa Err... 是吗? ;)
                    【解决方案20】:

                    以下代码有如下输出(结论是我们还可以通过分配更小的数组来节省一些内存资源):

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

                    【讨论】:

                      【解决方案21】:

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

                       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#。(PS:我知道这几乎是五年前的事了,但仍然...... ) 我使用 Telerik VB 到 C# 转换器,(反之亦然)并且代码工作得很好 - 不过不确定其他人。 (另一件事,converter.telerik.com
                      【解决方案22】:

                      最短的路只有 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();
                      }
                      

                      【讨论】:

                        【解决方案23】:

                        LINQ 的简单方法

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

                        【讨论】:

                          【解决方案24】:

                          另一种尝试通过减少分配来提高性能的方法,尤其是在多次调用此函数时。

                          它之所以有效,是因为您可以保证结果不会比输入长,因此可以传递输入和输出,而无需在内存中创建额外的副本。由于这个原因,您不能使用 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);
                          }
                          

                          【讨论】:

                            【解决方案25】:
                            public static string RemoveAllSpecialCharacters(this string text) {
                              if (string.IsNullOrEmpty(text))
                                return text;
                            
                              string result = Regex.Replace(text, "[:!@#$%^&*()}{|\":?><\\[\\]\\;'/.,~]", " ");
                              return result;
                            }
                            

                            【讨论】:

                            • 答案错误。如果你要使用正则表达式,它应该是包容性的,而不是排他性的,因为你现在错过了一些字符。实际上,正则表达式已经有了答案。并且要完整 - 正则表达式比较慢,然后直接比较字符函数。
                            【解决方案26】:

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

                            【讨论】:

                            • Noooooooooo!在 .NET 中更改字符串是 BAAAAAAAAAAAAD!框架中的一切都依赖于字符串是不可变的规则,如果你打破它,你会得到非常令人惊讶的副作用......
                            【解决方案27】:
                            public static string RemoveSpecialCharacters(string str){
                                return str.replaceAll("[^A-Za-z0-9_\\\\.]", "");
                            }
                            

                            【讨论】:

                            • 恐怕replaceAll不是C#字符串函数,而是Java或JavaScript
                            猜你喜欢
                            • 2011-04-11
                            • 2016-01-23
                            • 1970-01-01
                            • 2019-11-09
                            • 2011-08-29
                            相关资源
                            最近更新 更多