447

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

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

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

4

17 回答 17

745

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

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

在评论中注明@hypehuman,如果您打算多次执行此操作,请创建并存储一个 Regex 实例。这将节省每次构建它的开销,这比您想象的要昂贵。

private static readonly Regex sWhitespace = new Regex(@"\s+");
public static string ReplaceWhitespace(string input, string replacement) 
{
    return sWhitespace.Replace(input, replacement);
}
于 2011-06-02T19:38:29.887 回答
219

我有一个没有正则表达式的替代方法,它似乎表现得很好。这是 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 次尝试,第一个选项(没有 regexp)在不到一秒的时间内运行(在我的机器上为 700 毫秒),第二个需要 3.5 秒。

于 2013-01-29T19:58:52.750 回答
104

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

XML.Replace(" ", string.Empty);
于 2011-06-02T19:43:27.810 回答
99

我的解决方案是使用拆分和加入,它的速度非常快,实际上是这里的最佳答案中最快的。

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

带有空格的简单字符串上的 10,000 次循环的计时值 inc 新行和制表符

  • 拆分/加入 = 60 毫秒
  • linq chararray = 94 毫秒
  • 正则表达式 = 437 毫秒

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

public static string RemoveWhitespace(this string str) {
    return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
}
于 2015-06-09T13:02:00.273 回答
58

Henks 回答的基础上,我用他的回答创建了一些测试方法,并添加了一些更优化的方法。我发现结果因输入字符串的大小而异。因此,我用两个结果集进行了测试。在最快的方法中,链接源有更快的方法。但是,由于它被认为是不安全的,所以我把它省略了。

长输入字符串结果:

  1. InPlaceCharArray:2021 毫秒(Sunsetquest 的回答)-(原始来源
  2. 字符串拆分然后加入:4277 毫秒(Kernowcode 的答案
  3. 字符串阅读器:6082 毫秒
  4. 使用本机 char.IsWhitespace 的 LINQ:7357 毫秒
  5. LINQ:7746 毫秒(亨克的回答
  6. 循环:32320 毫秒
  7. 正则表达式编译:37157 毫秒
  8. 正则表达式:42940 毫秒

短输入字符串结果:

  1. InPlaceCharArray:108 毫秒(Sunsetquest 的回答)-(原始来源
  2. 字符串拆分然后加入:294 毫秒(Kernowcode 的答案
  3. 字符串阅读器:327 毫秒
  4. 循环:343 毫秒
  5. 使用本机 char.IsWhitespace 的 LINQ:624 毫秒
  6. LINQ:645ms(亨克的回答
  7. 正则表达式编译:1671 毫秒
  8. 正则表达式:2599 毫秒

代码

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 测试了一个不错的衬里。

于 2016-05-20T13:23:54.207 回答
31

只是一个替代方案,因为它看起来相当不错:) - 注意:Henks 的回答是其中最快的。

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

测试 1,000,000 个循环"This is a simple Test"

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

于 2013-11-28T05:28:18.190 回答
24

我在 Felipe Machado 的 CodeProject上找到了一篇很好的文章(在Richard Robertson的帮助下)

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

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

还有最快的不安全版本......(Sunsetquest 5/26/2021 的一些改进)

public static unsafe void RemoveAllWhitespace(ref 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;
            }
        }

        uint* pi = (uint*)pfixed;
        ulong len = ((ulong)dst - (ulong)pfixed) >> 1;
        pi[-1] = (uint)len;
        pfixed[len] = '\0';
    }
}

Stian Standahl 在 Stack Overflow 上也有一些不错的独立基准测试,这些基准测试还显示了 Felipe 的函数比第二快的函数快 300% 左右。另外,对于我修改的那个,我使用了这个技巧。

于 2016-05-21T21:25:52.523 回答
15

如果您需要出色的性能,则在这种情况下应避免使用 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);
    }
于 2013-12-31T13:08:06.083 回答
11

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

public static partial class Extension
{
    public static string RemoveWhiteSpace(this string self)
    {
        return new string(self.Where(c => !Char.IsWhiteSpace(c)).ToArray());
    }
}
于 2014-10-18T00:11:22.470 回答
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();
}
于 2011-06-02T19:50:51.300 回答
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();
}
于 2014-10-05T00:42:23.213 回答
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");
于 2011-06-02T19:40:28.280 回答
2

我们可以用:

    public static string RemoveWhitespace(this string input)
    {
        if (input == null)
            return null;
        return new string(input.ToCharArray()
            .Where(c => !Char.IsWhiteSpace(c))
            .ToArray());
    }
于 2016-06-20T14:59:52.437 回答
2

使用 Linq,您可以这样编写一个可读的方法:

    public static string RemoveAllWhitespaces(this string source)
    {
        return string.IsNullOrEmpty(source) ? source : new string(source.Where(x => !char.IsWhiteSpace(x)).ToArray());
    }
于 2020-08-04T11:07:15.823 回答
1

这是另一个变体:

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

与大多数其他解决方案一样,我没有进行详尽的基准测试,但这对于我的目的来说已经足够好了。

于 2015-06-18T19:49:43.783 回答
1

我想很多人来这里是为了消除空间。:

string s = "my string is nice";
s = s.replace(" ", "");
于 2022-01-12T11:05:26.347 回答
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 分钟后中止。
于 2015-02-02T21:53:01.877 回答