从String中删除所有空格的有效方法?

时间:2011-06-02 19:35:27

标签: c# removing-whitespace

我正在调用REST API并且正在接收XML响应。它返回一个工作区名称列表,我正在编写一个快速IsExistingWorkspace()方法。由于所有工作空间都包含没有空格的连续字符,因此我假设最简单的方法是查找列表中的特定工作空间是否删除所有空格(包括换行符)并执行此操作(XML是从Web接收的字符串请求):

XML.Contains("<name>" + workspaceName + "</name>");

我知道它区分大小写,我依赖于此。我只需要一种方法来有效地删除字符串中的所有空格。我知道RegEx和LINQ可以做到,但我对其他想法持开放态度。我大多只关心速度。

16 个答案:

答案 0 :(得分:518)

这是我所知道的最快的方式,即使你说你不想使用正则表达式:

Regex.Replace(XML, @"\s+", "")

答案 1 :(得分:161)

我有一种没有正则表达式的替代方法,它似乎表现得相当不错。这是Brandon Moretz回答的延续:

 public static string RemoveWhitespace(this string input)
 {
    return new string(input.ToCharArray()
        .Where(c => !Char.IsWhiteSpace(c))
        .ToArray());
 }

我在一个简单的单元测试中对它进行了测试:

[Test]
[TestCase("123 123 1adc \n 222", "1231231adc222")]
public void RemoveWhiteSpace1(string input, string expected)
{
    string s = null;
    for (int i = 0; i < 1000000; i++)
    {
        s = input.RemoveWhitespace();
    }
    Assert.AreEqual(expected, s);
}

[Test]
[TestCase("123 123 1adc \n 222", "1231231adc222")]
public void RemoveWhiteSpace2(string input, string expected)
{
    string s = null;
    for (int i = 0; i < 1000000; i++)
    {
        s = Regex.Replace(input, @"\s+", "");
    }
    Assert.AreEqual(expected, s);
}

对于1,000,000次尝试,第一个选项(没有正则表达式)在不到一秒的时间内运行(在我的机器上运行700毫秒),第二个选项需要3.5秒。

答案 2 :(得分:76)

在C#中尝试字符串的替换方法。

XML.Replace(" ", string.Empty);

答案 3 :(得分:55)

我的解决方案是使用Split和Join,它的速度非常快,实际上是最快的答案。

PreparedStatement stmt = conn.prepareStatement("INSERT INTO SCHEDULERS(SCHEDULER_ID, SCHEDULER_NAME, START_DATE,"
    + "FINISH_DATE, TIMER, EVENT_ID, BUILDING_ID, EVENT_PROPERTIES)"
    + " VALUES (?, ?, ?, ?, ?, ?, ?, ?)");

stmt.setString(1, object.getSchedulerId());
stmt.setString(2, object.getSchedulerName());
stmt.setDate(3, object.getStartDate());
stmt.setDate(4, object.getFinishDate());
stmt.setString(5, object.getTimer());
stmt.setString(6, object.getEvents().getEventId());
stmt.setString(7, object.getBuildingId());
stmt.setString(8, object.getEventProps());

stmt.executeUpdate();

对于带有空格的简单字符串的10,000循环计时,包括新行和制表符

  • split / join = 60毫秒
  • linq chararray = 94毫秒
  • regex = 437毫秒

通过将其包装在方法中来改善这一点,以便赋予它意义,并在我们处理它时使其成为一种扩展方法......

str = string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));

答案 4 :(得分:33)

构建于Henks answer我已经用他的答案和一些添加的,更优化的方法创建了一些测试方法。我发现结果根据输入字符串的大小而不同。因此,我测试了两个结果集。在最快的方法中,链接的源具有更快的方式。但是,由于它的特点是不安全,我把它留了出来。

长输入字符串结果:

  1. InPlaceCharArray:2021 ms(Sunsetquest's answer) - (Original source
  2. 字符串拆分然后加入:4277ms(Kernowcode's answer
  3. 字符串阅读器:6082毫秒
  4. LINQ使用native char.IsWhitespace:7357 ms
  5. LINQ:7746 ms(Henk's answer
  6. ForLoop:32320 ms
  7. Regex编译:37157 ms
  8. Regex:42940 ms
  9. 短输入字符串结果:

    1. InPlaceCharArray:108毫秒(Sunsetquest's answer) - (Original source
    2. 字符串拆分然后加入:294毫秒(Kernowcode's answer
    3. 字符串阅读器:327毫秒
    4. ForLoop:343 ms
    5. LINQ使用native char.IsWhitespace:624 ms
    6. LINQ:645ms(Henk's answer
    7. Regex编译:1671 ms
    8. Regex:2599 ms
    9. <强>代码

      public class RemoveWhitespace
      {
          public static string RemoveStringReader(string input)
          {
              var s = new StringBuilder(input.Length); // (input.Length);
              using (var reader = new StringReader(input))
              {
                  int i = 0;
                  char c;
                  for (; i < input.Length; i++)
                  {
                      c = (char)reader.Read();
                      if (!char.IsWhiteSpace(c))
                      {
                          s.Append(c);
                      }
                  }
              }
      
              return s.ToString();
          }
      
          public static string RemoveLinqNativeCharIsWhitespace(string input)
          {
              return new string(input.ToCharArray()
                  .Where(c => !char.IsWhiteSpace(c))
                  .ToArray());
          }
      
          public static string RemoveLinq(string input)
          {
              return new string(input.ToCharArray()
                  .Where(c => !Char.IsWhiteSpace(c))
                  .ToArray());
          }
      
          public static string RemoveRegex(string input)
          {
              return Regex.Replace(input, @"\s+", "");
          }
      
          private static Regex compiled = new Regex(@"\s+", RegexOptions.Compiled);
          public static string RemoveRegexCompiled(string input)
          {
              return compiled.Replace(input, "");
          }
      
          public static string RemoveForLoop(string input)
          {
              for (int i = input.Length - 1; i >= 0; i--)
              {
                  if (char.IsWhiteSpace(input[i]))
                  {
                      input = input.Remove(i, 1);
                  }
              }
              return input;
          }
      
          public static string StringSplitThenJoin(this string str)
          {
              return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
          }
      
          public static string RemoveInPlaceCharArray(string input)
          {
              var len = input.Length;
              var src = input.ToCharArray();
              int dstIdx = 0;
              for (int i = 0; i < len; i++)
              {
                  var ch = src[i];
                  switch (ch)
                  {
                      case '\u0020':
                      case '\u00A0':
                      case '\u1680':
                      case '\u2000':
                      case '\u2001':
                      case '\u2002':
                      case '\u2003':
                      case '\u2004':
                      case '\u2005':
                      case '\u2006':
                      case '\u2007':
                      case '\u2008':
                      case '\u2009':
                      case '\u200A':
                      case '\u202F':
                      case '\u205F':
                      case '\u3000':
                      case '\u2028':
                      case '\u2029':
                      case '\u0009':
                      case '\u000A':
                      case '\u000B':
                      case '\u000C':
                      case '\u000D':
                      case '\u0085':
                          continue;
                      default:
                          src[dstIdx++] = ch;
                          break;
                  }
              }
              return new string(src, 0, dstIdx);
          }
      }
      

      <强>测试

      [TestFixture]
      public class Test
      {
          // Short input
          //private const string input = "123 123 \t 1adc \n 222";
          //private const string expected = "1231231adc222";
      
          // Long input
          private const string input = "123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222";
          private const string expected = "1231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc222";
      
          private const int iterations = 1000000;
      
          [Test]
          public void RemoveInPlaceCharArray()
          {
              string s = null;
              var stopwatch = Stopwatch.StartNew();
              for (int i = 0; i < iterations; i++)
              {
                  s = RemoveWhitespace.RemoveInPlaceCharArray(input);
              }
      
              stopwatch.Stop();
              Console.WriteLine("InPlaceCharArray: " + stopwatch.ElapsedMilliseconds + " ms");
              Assert.AreEqual(expected, s);
          }
      
          [Test]
          public void RemoveStringReader()
          {
              string s = null;
              var stopwatch = Stopwatch.StartNew();
              for (int i = 0; i < iterations; i++)
              {
                  s = RemoveWhitespace.RemoveStringReader(input);
              }
      
              stopwatch.Stop();
              Console.WriteLine("String reader: " + stopwatch.ElapsedMilliseconds + " ms");
              Assert.AreEqual(expected, s);
          }
      
          [Test]
          public void RemoveLinqNativeCharIsWhitespace()
          {
              string s = null;
              var stopwatch = Stopwatch.StartNew();
              for (int i = 0; i < iterations; i++)
              {
                  s = RemoveWhitespace.RemoveLinqNativeCharIsWhitespace(input);
              }
      
              stopwatch.Stop();
              Console.WriteLine("LINQ using native char.IsWhitespace: " + stopwatch.ElapsedMilliseconds + " ms");
              Assert.AreEqual(expected, s);
          }
      
          [Test]
          public void RemoveLinq()
          {
              string s = null;
              var stopwatch = Stopwatch.StartNew();
              for (int i = 0; i < iterations; i++)
              {
                  s = RemoveWhitespace.RemoveLinq(input);
              }
      
              stopwatch.Stop();
              Console.WriteLine("LINQ: " + stopwatch.ElapsedMilliseconds + " ms");
              Assert.AreEqual(expected, s);
          }
      
          [Test]
          public void RemoveRegex()
          {
              string s = null;
              var stopwatch = Stopwatch.StartNew();
              for (int i = 0; i < iterations; i++)
              {
                  s = RemoveWhitespace.RemoveRegex(input);
              }
      
              stopwatch.Stop();
              Console.WriteLine("Regex: " + stopwatch.ElapsedMilliseconds + " ms");
      
              Assert.AreEqual(expected, s);
          }
      
          [Test]
          public void RemoveRegexCompiled()
          {
              string s = null;
              var stopwatch = Stopwatch.StartNew();
              for (int i = 0; i < iterations; i++)
              {
                  s = RemoveWhitespace.RemoveRegexCompiled(input);
              }
      
              stopwatch.Stop();
              Console.WriteLine("RegexCompiled: " + stopwatch.ElapsedMilliseconds + " ms");
      
              Assert.AreEqual(expected, s);
          }
      
          [Test]
          public void RemoveForLoop()
          {
              string s = null;
              var stopwatch = Stopwatch.StartNew();
              for (int i = 0; i < iterations; i++)
              {
                  s = RemoveWhitespace.RemoveForLoop(input);
              }
      
              stopwatch.Stop();
              Console.WriteLine("ForLoop: " + stopwatch.ElapsedMilliseconds + " ms");
      
              Assert.AreEqual(expected, s);
          }
      
          [TestMethod]
          public void StringSplitThenJoin()
          {
              string s = null;
              var stopwatch = Stopwatch.StartNew();
              for (int i = 0; i < iterations; i++)
              {
                  s = RemoveWhitespace.StringSplitThenJoin(input);
              }
      
              stopwatch.Stop();
              Console.WriteLine("StringSplitThenJoin: " + stopwatch.ElapsedMilliseconds + " ms");
      
              Assert.AreEqual(expected, s);
          }
      }
      

      编辑:从Kernowcode测试了一个漂亮的衬里。

答案 5 :(得分:23)

只是另一种选择,因为它看起来很不错:) - 注意:Henks answer是最快的。

input.ToCharArray()
 .Where(c => !Char.IsWhiteSpace(c))
 .Select(c => c.ToString())
 .Aggregate((a, b) => a + b);

"This is a simple Test"

上测试1,000,000个循环

此方法= 1.74秒
正则表达式= 2.58秒
new String(Henks)= 0.82

答案 6 :(得分:17)

我在Felcel Machado的CodeProject上找到了a nice write-up on this(在Richard Robertson的帮助下)

他测试了十种不同的方法。这是最快的 不安全 版本......

public static unsafe string TrimAllWithStringInplace(string str) {
    fixed (char* pfixed = str) {
        char* dst = pfixed;
        for (char* p = pfixed; *p != 0; p++)

            switch (*p) {

                case '\u0020': case '\u00A0': case '\u1680': case '\u2000': case '\u2001':

                case '\u2002': case '\u2003': case '\u2004': case '\u2005': case '\u2006':

                case '\u2007': case '\u2008': case '\u2009': case '\u200A': case '\u202F':

                case '\u205F': case '\u3000': case '\u2028': case '\u2029': case '\u0009':

                case '\u000A': case '\u000B': case '\u000C': case '\u000D': case '\u0085':
                    continue;

                default:
                    *dst++ = *p;
                    break;
    }

    return new string(pfixed, 0, (int)(dst - pfixed));
}

最快的 安全 版本......

public static string TrimAllWithInplaceCharArray(string str) {

    var len = str.Length;
    var src = str.ToCharArray();
    int dstIdx = 0;

    for (int i = 0; i < len; i++) {
        var ch = src[i];

        switch (ch) {

            case '\u0020': case '\u00A0': case '\u1680': case '\u2000': case '\u2001':

            case '\u2002': case '\u2003': case '\u2004': case '\u2005': case '\u2006':

            case '\u2007': case '\u2008': case '\u2009': case '\u200A': case '\u202F':

            case '\u205F': case '\u3000': case '\u2028': case '\u2029': case '\u0009':

            case '\u000A': case '\u000B': case '\u000C': case '\u000D': case '\u0085':
                continue;

            default:
                src[dstIdx++] = ch;
                break;
        }
    }
    return new string(src, 0, dstIdx);
}

Stian Standahl在Stack Overflow上也有一些不错的independent benchmarks,它也展示了Felipe的功能如何比下一个最快的功能快300%。

答案 7 :(得分:13)

如果您需要卓越的性能,在这种情况下应避免使用LINQ和正则表达式。我做了一些性能基准测试,看起来如果你想从字符串的开头和结尾去掉空格,string.Trim()就是你的终极功能。

如果您需要从字符串中删除所有空格,则以下方法在此处发布的所有内容中运行速度最快:

    public static string RemoveWhitespace(this string input)
    {
        int j = 0, inputlen = input.Length;
        char[] newarr = new char[inputlen];

        for (int i = 0; i < inputlen; ++i)
        {
            char tmp = input[i];

            if (!char.IsWhiteSpace(tmp))
            {
                newarr[j] = tmp;
                ++j;
            }
        }
        return new String(newarr, 0, j);
    }

答案 8 :(得分:7)

正则表达式过度;只需在字符串上使用扩展名(感谢Henk)。这是微不足道的,应该是框架的一部分。无论如何,这是我的实施:

public static partial class Extension
{
    public static string RemoveWhiteSpace(this string self)
    {
        return new string(self.Where(c => !Char.IsWhiteSpace(c)).ToArray());
    }
}

答案 9 :(得分:4)

以下是RegEx解决方案的简单线性替代方案。我不确定哪个更快;你必须对它进行基准测试。

static string RemoveWhitespace(string input)
{
    StringBuilder output = new StringBuilder(input.Length);

    for (int index = 0; index < input.Length; index++)
    {
        if (!Char.IsWhiteSpace(input, index))
        {
            output.Append(input[index]);
        }
    }
    return output.ToString();
}

答案 10 :(得分:3)

我需要用空格替换字符串中的空格,而不是重复空格。例如,我需要转换如下内容:

"a b   c\r\n d\t\t\t e"

"a b c d e"

我使用了以下方法

private static string RemoveWhiteSpace(string value)
{
    if (value == null) { return null; }
    var sb = new StringBuilder();

    var lastCharWs = false;
    foreach (var c in value)
    {
        if (char.IsWhiteSpace(c))
        {
            if (lastCharWs) { continue; }
            sb.Append(' ');
            lastCharWs = true;
        }
        else
        {
            sb.Append(c);
            lastCharWs = false;
        }
    }
    return sb.ToString();
}

答案 11 :(得分:2)

我假设您的XML响应如下所示:

var xml = @"<names>
                <name>
                    foo
                </name>
                <name>
                    bar
                </name>
            </names>";

处理XML的最佳方法是使用XML解析器,例如 LINQ to XML

var doc = XDocument.Parse(xml);

var containsFoo = doc.Root
                     .Elements("name")
                     .Any(e => ((string)e).Trim() == "foo");

答案 12 :(得分:1)

这是另一种变体:

public static string RemoveAllWhitespace(string aString)
{
  return String.Join(String.Empty, aString.Where(aChar => aChar !Char.IsWhiteSpace(aChar)));
}

与大多数其他解决方案一样,我没有进行详尽的基准测试,但这对我的目的来说效果很好。

答案 13 :(得分:0)

我发现不同的结果是真实的。我试图用一个空格替换所有空格,正则表达式非常慢。

return( Regex::Replace( text, L"\s+", L" " ) );

对我来说最有效的(在C ++ cli中)是:

String^ ReduceWhitespace( String^ text )
{
  String^ newText;
  bool    inWhitespace = false;
  Int32   posStart = 0;
  Int32   pos      = 0;
  for( pos = 0; pos < text->Length; ++pos )
  {
    wchar_t cc = text[pos];
    if( Char::IsWhiteSpace( cc ) )
    {
      if( !inWhitespace )
      {
        if( pos > posStart ) newText += text->Substring( posStart, pos - posStart );
        inWhitespace = true;
        newText += L' ';
      }
      posStart = pos + 1;
    }
    else
    {
      if( inWhitespace )
      {
        inWhitespace = false;
        posStart = pos;
      }
    }
  }

  if( pos > posStart ) newText += text->Substring( posStart, pos - posStart );

  return( newText );
}

我首先通过单独替换每个字符来尝试上述例程,但是必须切换到非空格部分的子字符串。申请1,200,000字符串时:

  • 以上例程在25秒内完成
  • 以上例程+ 95秒内单独替换字符
  • 15分钟后正则表达式中止。

答案 14 :(得分:0)

我们可以使用:

    public static string RemoveWhitespace(this string input)
    {
        if (input == null)
            return null;
        return new string(input.ToCharArray()
            .Where(c => !Char.IsWhiteSpace(c))
            .ToArray());
    }

答案 15 :(得分:0)

使用Linq,您可以通过以下方式编写可读的方法:

    public static string RemoveAllWhitespaces(this string source)
    {
        return string.IsNullOrEmpty(source) ? source : new string(source.Where(x => !char.IsWhiteSpace(x)).ToArray());
    }