【问题标题】:How do I replace multiple spaces with a single space in C#?如何在 C# 中用单个空格替换多个空格?
【发布时间】:2008-10-15 22:10:31
【问题描述】:

如何在 C# 中用一个空格替换字符串中的多个空格?

例子:

1 2 3  4    5

应该是:

1 2 3 4 5

【问题讨论】:

  • 一个状态机可以很容易地做到这一点,但如果你只需要它来删除空格,它可能是矫枉过正
  • 我在重复的问题stackoverflow.com/a/37592018/582061 中添加了一个关于不同方法的基准。正则表达式并不是最快的方法。

标签: c# regex string


【解决方案1】:

我喜欢用:

myString = Regex.Replace(myString, @"\s+", " ");

因为它会捕获任何类型的空格(例如制表符、换行符等)并用单个空格替换它们。

【讨论】:

  • 轻微修改:Regex.Replace(source, @"(\s)\s+", "$1");这将返回找到的第一个空格类型。因此,如果您有 5 个选项卡,它将返回一个选项卡。以防有人喜欢这个。
  • @radistao 您的链接用于 Javascript 字符串替换,不适用于 C#。
  • @Shiva, /\s\s+/ 是一个标准的 POSIX 正则表达式语句,可以使用自己的语法转换/使用任何语言
  • 本着@F.B.tenKate 解决方案的精神:Regex.Replace(source, @"(\s)\1+", "$1");会将多个相同的个连续字符替换为一个字符。
  • 为了删除前导和尾随空格,您应该使用 Trim() 函数,例如 var myString = Regex.Replace(myString, @"\s+", " ").Trim() ;
【解决方案2】:
string sentence = "This is a sentence with multiple    spaces";
RegexOptions options = RegexOptions.None;
Regex regex = new Regex("[ ]{2,}", options);     
sentence = regex.Replace(sentence, " ");

【讨论】:

  • 我已经复制并粘贴了它,它可以工作。我真的不喜欢正则表达式,但这次它救了我的命。
  • @Craig 一个评论就足够了,IMO。 // 这个块用一个替换多个空格... :)
  • 真的,RegEx 在这方面有点矫枉过正。
  • @Joel:不能同意。实际上,我确信这种方式对于足够大的字符串比您的方式更有效,并且可以在一行中完成。哪里矫枉过正?
  • @Oscar Joel 的代码不是遍历所有字符的简单循环!这是一个隐藏的嵌套循环,具有二次最坏情况。相比之下,这个正则表达式是线性的,只构建一个字符串(= 与 Joel 的代码相比,分配成本大大降低),而且引擎可以优化它(老实说,我怀疑 .NET 正则表达式是足够聪明,但理论上这个正则表达式可以很便宜地实现,它甚至不再有趣;它只需要一个具有三个状态的 DFA,每个状态一个转换,并且没有额外的信息)。
【解决方案3】:
string xyz = "1   2   3   4   5";
xyz = string.Join( " ", xyz.Split( new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries ));

【讨论】:

  • 这比正则表达式更具可读性,我更喜欢它,因为我不需要学习其他语法
  • 我喜欢它,因为它不需要正则表达式
  • 这对于大字符串来说效率很低。
  • 这也会删除前导和尾随空格。
  • 我也更喜欢这个答案。我的老导师曾经说过“任何时候你认为你需要正则表达式来解决一个问题,嗯......现在你有两个问题”
【解决方案4】:

我认为马特的答案是最好的,但我不认为这是完全正确的。如果要替换换行符,必须使用:

myString = Regex.Replace(myString, @"\s+", " ", RegexOptions.Multiline);

【讨论】:

  • RegexOptions.Multiline 改变了 ^ 和 $ 的含义,因此它们匹配每一行的开头和结尾 ($ = \n),而不是整个多行字符串。因为 \s 等价于 [ \f\n\r\t\v],所以即使 Multiline 选项关闭,也应该替换换行符。
  • 马特的回答已经涵盖了这一点。我“相信”有 30 个人只是蒙上眼睛对这个答案投了赞成票 :)
【解决方案5】:

另一种使用 LINQ 的方法:

 var list = str.Split(' ').Where(s => !string.IsNullOrWhiteSpace(s));
 str = string.Join(" ", list);

【讨论】:

  • 喜欢这个解决方案!这个 2022 年是否有不利之处,或者为什么它不更受欢迎。
【解决方案6】:

这比这一切都简单:

while(str.Contains("  ")) str = str.Replace("  ", " ");

【讨论】:

  • 如果字符串包含 3 个或更多空格的序列,这将远低于正则表达式“{2,}”。
  • @JanGoyvaerts:即使有 10 个空格,当我进行快速而肮脏的测试时,正则表达式也会变慢。话虽如此,只需要一个充满空格的巨大子字符串就可以完全破坏 while 循环的性能。为了公平起见,我使用了我使用 RegexOptions.Compiled,而不是较慢的 Regex.Replace。
  • RegexOptions.Compiled 增加了将正则表达式编译到 IL 中的大量开销。不要使用它,除非您的应用程序会经常使用正则表达式或在足够大的字符串上使用正则表达式,从而提高的匹配速度抵消了降低的编译速度。
  • 这是一个极端低效的代码示例。哈哈。
  • @pcbabu 在许多情况下,它并不像看起来那么糟糕。 Replace() 方法将处理给定字符串中所有出现的两个空格,因此我们不会为字符串中配对空格的每个实例循环(并重新分配整个字符串)。一个新的分配将处理所有这些。我们只在有 3 个或更多空格在一起时才重新运行循环,这对于许多输入源来说可能很少发生。如果你能证明它对你的数据来说是个问题,那么就去编写状态机,将一个字符一个字符地推送到一个新的字符串构建器中。
【解决方案7】:

即使是简单的任务,正则表达式也会相当慢。这将创建一个扩展方法,可以在任何string 之外使用。

    public static class StringExtension
    {
        public static String ReduceWhitespace(this String value)
        {
            var newString = new StringBuilder();
            bool previousIsWhitespace = false;
            for (int i = 0; i < value.Length; i++)
            {
                if (Char.IsWhiteSpace(value[i]))
                {
                    if (previousIsWhitespace)
                    {
                        continue;
                    }

                    previousIsWhitespace = true;
                }
                else
                {
                    previousIsWhitespace = false;
                }

                newString.Append(value[i]);
            }

            return newString.ToString();
        }
    }

它会被这样使用:

string testValue = "This contains     too          much  whitespace."
testValue = testValue.ReduceWhitespace();
// testValue = "This contains too much whitespace."

【讨论】:

  • 我喜欢扩展方法的想法,尽管可以优化例程。
【解决方案8】:
myString = Regex.Replace(myString, " {2,}", " ");

【讨论】:

    【解决方案9】:

    对于那些不喜欢Regex的人,这里有一个使用StringBuilder的方法:

        public static string FilterWhiteSpaces(string input)
        {
            if (input == null)
                return string.Empty;
    
            StringBuilder stringBuilder = new StringBuilder(input.Length);
            for (int i = 0; i < input.Length; i++)
            {
                char c = input[i];
                if (i == 0 || c != ' ' || (c == ' ' && input[i - 1] != ' '))
                    stringBuilder.Append(c);
            }
            return stringBuilder.ToString();
        }
    

    在我的测试中,与静态编译的 Regex 相比,这种方法在处理大量中小型字符串时平均快 16 倍。与非编译或非静态正则表达式相比,这应该更快。

    请记住,它不会删除前导或尾随空格,只会删除多次出现的空格。

    【讨论】:

    【解决方案10】:

    这是一个较短的版本,仅当您只执行一次时才应使用它,因为它每次调用时都会创建 Regex 类的新实例。

    temp = new Regex(" {2,}").Replace(temp, " "); 
    

    如果你对正则表达式不太熟悉,这里有一个简短的解释:

    {2,} 使正则表达式搜索它前面的字符,并找到 2 次到无限次之间的子字符串。
    .Replace(temp, " ") 用空格替换字符串 temp 中的所有匹配项。

    如果您想多次使用它,这里是一个更好的选择,因为它会在编译时创建正则表达式 IL:

    Regex singleSpacify = new Regex(" {2,}", RegexOptions.Compiled);
    temp = singleSpacify.Replace(temp, " ");
    

    【讨论】:

      【解决方案11】:

      您可以在一个解决方案中简单地做到这一点!

      string s = "welcome to  london";
      s.Replace(" ", "()").Replace(")(", "").Replace("()", " ");
      

      如果你愿意,你可以选择其他括号(甚至是其他字符)。

      【讨论】:

      • 你必须确保你的字符串中没有“()”或“)(”。或者"wel()come to london)("变成"wel come to london"。你可以尝试使用很多括号。所以使用((((())))) 而不是 ())))))((((( 而不是 )(。它仍然可以工作。不过,如果字符串包含 ((((())))))))))(((((,这将失败。
      【解决方案12】:

      没有 Regex,没有 Linq...删除前导和尾随空格,并将任何嵌入的多个空格段减少到一个空格

      string myString = "   0 1 2  3   4               5  ";
      myString = string.Join(" ", myString.Split(new char[] { ' ' }, 
      StringSplitOptions.RemoveEmptyEntries));
      

      结果:“0 1 2 3 4 5”

      【讨论】:

      • 提醒一句:使用拆分虽然非常容易理解,但会对性能产生惊人的负面影响。由于可以创建许多字符串,因此您必须注意内存使用情况,以防使用此方法处理大字符串。
      【解决方案13】:

      根据 Joel 的说法,安慰其他答案,并希望随着我的进展略有改善:

      您可以使用Regex.Replace()

      string s = Regex.Replace (
          "   1  2    4 5", 
          @"[ ]{2,}", 
          " "
          );
      

      或者String.Split():

      static class StringExtensions
      {
          public static string Join(this IList<string> value, string separator)
          {
              return string.Join(separator, value.ToArray());
          }
      }
      
      //...
      
      string s = "     1  2    4 5".Split (
          " ".ToCharArray(), 
          StringSplitOptions.RemoveEmptyEntries
          ).Join (" ");
      

      【讨论】:

        【解决方案14】:
        // Mysample string
        string str ="hi you           are          a demo";
        
        //Split the words based on white sapce
        var demo= str .Split(' ').Where(s => !string.IsNullOrWhiteSpace(s));
                
        //Join the values back and add a single space in between
        str = string.Join(" ", demo);
        // output: string str ="hi you are a demo";
        

        【讨论】:

          【解决方案15】:

          我刚刚写了一个我喜欢的新Join,所以我想我会重新回答:

          public static string Join<T>(this IEnumerable<T> source, string separator)
          {
              return string.Join(separator, source.Select(e => e.ToString()).ToArray());
          }
          

          其中一个很酷的事情是它可以通过在元素上调用 ToString() 来处理非字符串的集合。用法还是一样的:

          //...
          
          string s = "     1  2    4 5".Split (
              " ".ToCharArray(), 
              StringSplitOptions.RemoveEmptyEntries
              ).Join (" ");
          

          【讨论】:

          • 为什么要创建扩展方法?为什么不直接使用 string.Join()?
          【解决方案16】:

          许多答案都提供了正确的输出,但对于那些寻求最佳性能的人,我确实将Nolanar's answer(这是性能的最佳答案)提高了大约 10%。

          public static string MergeSpaces(this string str)
          {
          
              if (str == null)
              {
                  return null;
              }
              else
              {
                  StringBuilder stringBuilder = new StringBuilder(str.Length);
          
                  int i = 0;
                  foreach (char c in str)
                  {
                      if (c != ' ' || i == 0 || str[i - 1] != ' ')
                          stringBuilder.Append(c);
                      i++;
                  }
                  return stringBuilder.ToString();
              }
          
          }
          

          【讨论】:

            【解决方案17】:

            使用正则表达式模式

                [ ]+    #only space
            
               var text = Regex.Replace(inputString, @"[ ]+", " ");
            

            【讨论】:

              【解决方案18】:

              我知道这已经很老了,但是在尝试完成几乎相同的事情时遇到了这个问题。在 RegEx Buddy 中找到了这个解决方案。此模式将用单个空格替换所有双空格,并修剪前导和尾随空格。

              pattern: (?m:^ +| +$|( ){2,})
              replacement: $1
              

              它有点难以阅读,因为我们正在处理空白,所以这里再次将“空格”替换为“_”。

              pattern: (?m:^_+|_+$|(_){2,})  <-- don't use this, just for illustration.
              

              “(?m:”构造启用“多行”选项。我通常喜欢在模式本身中包含我可以使用的任何选项,以便它更加独立。

              【讨论】:

                【解决方案19】:

                我可以用这个删除空格

                while word.contains("  ")  //double space
                   word = word.Replace("  "," "); //replace double space by single space.
                word = word.trim(); //to remove single whitespces from start & end.
                

                【讨论】:

                • 是的,但您只能用一个替换两个空格。这对 X 个空格没有帮助
                • While 循环会处理所有要删除的双空格。
                【解决方案20】:

                不使用正则表达式:

                while (myString.IndexOf("  ", StringComparison.CurrentCulture) != -1)
                {
                    myString = myString.Replace("  ", " ");
                }
                

                可以在短字符串上使用,但在带有大量空格的长字符串上表现不佳。

                【讨论】:

                  【解决方案21】:

                  试试这个方法

                  private string removeNestedWhitespaces(char[] st)
                  {
                      StringBuilder sb = new StringBuilder();
                      int indx = 0, length = st.Length;
                      while (indx < length)
                      {
                          sb.Append(st[indx]);
                          indx++;
                          while (indx < length && st[indx] == ' ')
                              indx++;
                          if(sb.Length > 1  && sb[0] != ' ')
                              sb.Append(' ');
                      }
                      return sb.ToString();
                  }
                  

                  像这样使用它:

                  string test = removeNestedWhitespaces("1 2 3  4    5".toCharArray());
                  

                  【讨论】:

                  • 这将删除尾随空格
                  • 对不起,错误,我修复了代码,现在它按预期工作测试字符串:“1 2 3 4 9”结果字符串:“1 2 3 4 9”
                  【解决方案22】:

                  这是对Nolonar original answer轻微修改

                  检查字符是否不仅仅是一个空格,而是任何空格,使用这个:

                  它将用单个空格替换任何多个空格字符。

                  public static string FilterWhiteSpaces(string input)
                  {
                      if (input == null)
                          return string.Empty;
                  
                      var stringBuilder = new StringBuilder(input.Length);
                      for (int i = 0; i < input.Length; i++)
                      {
                          char c = input[i];
                          if (i == 0 || !char.IsWhiteSpace(c) || (char.IsWhiteSpace(c) && 
                              !char.IsWhiteSpace(strValue[i - 1])))
                              stringBuilder.Append(c);
                      }
                      return stringBuilder.ToString();
                  }
                  

                  【讨论】:

                  • 谢谢,这帮助了我。小错误:strValue 应该是input。此外,IsWhiteSpace 包括换行符。您可能不想合并多个换行符,只要它会根据您的环境表现不同(\r\n vs \n)。在这种情况下,请检查“CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.SpaceSeparator”。
                  • @OliverSchimmer 是的,感谢您的更正。添加的 unicode 字符信息是一个很好的补充。随时进行编辑! :)
                  • 这不是对这个答案的重写吗? stackoverflow.com/a/33817748/56621
                  • @AlexfromJitbit,它实际上是对 my answer 的修改,比其他答案早了大约 2.5 年。
                  • @Nolonar 是的,我承认在我的回答中,希望没问题
                  【解决方案23】:

                  老派:

                  string oldText = "   1 2  3   4    5     ";
                  string newText = oldText
                                      .Replace("  ", " " + (char)22 )
                                      .Replace( (char)22 + " ", "" )
                                      .Replace( (char)22 + "", "" );
                  
                  Assert.That( newText, Is.EqualTo( " 1 2 3 4 5 " ) );
                  

                  【讨论】:

                    【解决方案24】:

                    StringBuilderEnumerable.Aggregate() 的混合作为字符串的扩展方法:

                    using System;
                    using System.Linq;
                    using System.Text;
                    
                    public static class StringExtension
                    {
                        public static string StripSpaces(this string s)
                        {
                            return s.Aggregate(new StringBuilder(), (acc, c) =>
                            {
                                if (c != ' ' || acc.Length > 0 && acc[acc.Length-1] != ' ')
                                    acc.Append(c);
                    
                                return acc;
                            }).ToString();
                        }
                    
                        public static void Main()
                        {
                            Console.WriteLine("\"" + StringExtension.StripSpaces("1   Hello       World  2   ") + "\"");
                        }
                    }
                    

                    输入:

                    "1   Hello       World  2   "
                    

                    输出:

                    "1 Hello World 2 "
                    

                    【讨论】:

                      【解决方案25】:

                      我查看了建议的解决方案,找不到可以处理我的情况可接受的混合空白字符的解决方案,例如:

                      • Regex.Replace(input, @"\s+", " ") - 如果它们与空格混合,它将吃掉你的换行符,例如 \n \n 序列将被替换为
                      • Regex.Replace(source, @"(\s)\s+", "$1") - 这将取决于空格的第一个字符,这意味着它可能会再次吃掉你的换行符
                      • Regex.Replace(source, @"[ ]{2,}", " ") - 当混合了空白字符时,它将无法正常工作 - 例如 "\t \t "

                      可能并不完美,但对我来说快速的解决方案是:

                      Regex.Replace(input, @"\s+", 
                      (match) => match.Value.IndexOf('\n') > -1 ? "\n" : " ", RegexOptions.Multiline)
                      

                      想法是 - 换行胜过空格和制表符。

                      这将无法正确处理 windows 换行符,但它也很容易调整以使用它,不太了解正则表达式 - 可能适合单一模式。

                      【讨论】:

                        【解决方案26】:

                        去流氓怎么样?

                        public static string MinimizeWhiteSpace(
                            this string _this)
                            {
                                if (_this != null)
                                {
                                    var returned = new StringBuilder();
                                    var inWhiteSpace = false;
                                    var length = _this.Length;
                                    for (int i = 0; i < length; i++)
                                    {
                                        var character = _this[i];
                                        if (char.IsWhiteSpace(character))
                                        {
                                            if (!inWhiteSpace)
                                            {
                                                inWhiteSpace = true;
                                                returned.Append(' ');
                                            }
                                        }
                                        else
                                        {
                                            inWhiteSpace = false;
                                            returned.Append(character);
                                        }
                                    }
                                    return returned.ToString();
                                }
                                else
                                {
                                    return null;
                                }
                            }
                        

                        【讨论】:

                          【解决方案27】:

                          以下代码将所有多个空格删除为一个空格

                              public string RemoveMultipleSpacesToSingle(string str)
                              {
                                  string text = str;
                                  do
                                  {
                                      //text = text.Replace("  ", " ");
                                      text = Regex.Replace(text, @"\s+", " ");
                                  } while (text.Contains("  "));
                                  return text;
                              }
                          

                          【讨论】:

                            【解决方案28】:

                            您可以使用 RemoveDoubleSpaces() 之类的方法创建 StringsExtensions 文件。

                            StringsExtentions.cs

                            public static string RemoveDoubleSpaces(this string value)  
                            {
                              while (value.Contains("  "))
                              {
                                value = value.Replace("  ", " ");
                              }
                            
                              // this removes space at the end of the value (like "demo ")
                              if (value.EndsWith(" ")) value = value.Remove(value.Length - 1);
                            
                              // this removes space at the start of the value (like " hi")
                              if (value.StartsWith(" ")) value = value[1..];
                              
                              return value;
                            }
                            

                            然后你可以像这样使用它:

                            string stringInput =" hi here     is  a demo ";
                            
                            string stringCleaned = stringInput.RemoveDoubleSpaces();
                            

                            【讨论】:

                            • 这是非常低效的。如果输入包含 8 个连续空格,则第一个循环将运行 3 次。第一个中的StartsWith 必须搜索整个字符串以获得false,如果字符串很大,则可能需要一些时间。第二个和第三个循环是不必要的,第一个循环意味着最多可以有一个初始空间和一个最终空间。
                            • 感谢 cmets @AdrianHHH 。我根据您的提示进行了一些快速改进。一旦我有一些空闲时间,我将在更深的阶段检查它以进行优化。最佳
                            • 赋值value = value.Replace(" ", " "); 在堆上分配一个新字符串来保存替换的结果,然后旧字符串等待被垃圾回收。如果初始输入有 32 个连续的空格,那么循环将运行 5 次,这意味着将有 5 次分配新字符串和 5 个旧字符串被释放并准备好进行垃圾回收。最后两个修改(用于前导和尾随空格)也将分配新字符串和释放旧字符串,如果执行的话。
                            • 作为一个学习练习,我建议您创建一个程序来对该问题的许多答案中显示的各种方法进行基准测试。查找执行时间和内存(堆)使用情况。在这里把它写成一个新的答案可能会很好。 (看看“BenchmarkDotNet”。)
                            • @AdrianHHH 感谢您的 cmets。记忆很重要,但“易于阅读/遵循代码”对我来说更重要。在我实施它的项目中,对结果没有负面影响,因此我更愿意保持原样并专注于下一个功能。无论如何,我都会检查 BenchmarkDotNet。
                            猜你喜欢
                            • 1970-01-01
                            • 2013-05-30
                            • 2013-11-25
                            • 1970-01-01
                            • 2017-03-08
                            • 2010-11-19
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多