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

Cor*_*urn 328 c# removing-whitespace

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

XML.Contains("<name>" + workspaceName + "</name>");
Run Code Online (Sandbox Code Playgroud)

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

sla*_*dau 562

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

Regex.Replace(XML, @"\s+", "")
Run Code Online (Sandbox Code Playgroud)

  • 如果您计划多次执行此操作,请创建并存储Regex实例.这将节省每次构建它的开销,这比你想象的要贵.`private static readonly Regex sWhitespace = new Regex(@"\ s +"); public static string ReplaceWhitespace(string input,string replacement){return sWhitespace.Replace(input,replacement); }` (53认同)
  • 不应该是`Regex.Replace(XML,@"\ s +","")`? (17认同)
  • 对于RegEx的新手并寻求对该表达式的含义的解释,“ \ s”表示“匹配任何空白标记”,而“ +”表示“匹配一个或多个进行中的标记”。如果您想尝试,[RegExr](https://regexr.com/)也是一个不错的网站,可用来练习编写RegEx表达式。 (8认同)
  • 我可以使用正则表达式,我只是不确定它是否是最快的方法。 (3认同)
  • 到目前为止,使用拆分/连接组合进行测试是最快的,请参阅下面的 KernowCode 答案。 (3认同)

Hen*_*amp 168

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

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

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

[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);
}
Run Code Online (Sandbox Code Playgroud)

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

  • `.ToCharArray()`不是必需的; 你可以直接在字符串上使用`.Where()`. (37认同)
  • @ppumkin他要求一次删除空格.不是其他处理的多次迭代.我不打算将这个单行空格删除到关于基准文本处理的扩展帖子中. (17认同)
  • 请注意这里.正则表达式在小字符串上更慢!如果你说你有一个美国税法卷的数字化版本(〜百万字?),经过几次迭代,到目前为止,正则表达是王道!它不是更快,但在什么情况下应该使用什么.你在这里只证明了一半.-1直到您证明测试的后半部分,以便答案提供更多洞察时间应该使用什么. (6认同)
  • @ProgramFOX,在另一个问题(无法轻易找到它)中,我注意到至少在某些查询中,对字符串直接使用`ToCharArray`比使用`.Where()`要快。这与每个迭代步骤中IEnumerable &lt;&gt;的开销有关,并且ToCharArray的效率很高(块复制),并且编译器会优化数组的迭代。为什么存在这种差异,没有人能够解释我,但是在删除`ToCharArray()`之前先进行测量。 (2认同)

Mik*_*e_K 82

在C#中尝试字符串的replace方法.

XML.Replace(" ", string.Empty);
Run Code Online (Sandbox Code Playgroud)

  • 不删除制表符或换行符.如果我现在多次删除我正在对字符串进行多次传递. (27认同)
  • 正如slandau和Henk的回答那样,不要删除所有空格. (11认同)
  • @Zapnologica它只替换空格字符.OP要求更换换行符(即使它们不是空格字符,也是"空白"字符). (4认同)

ker*_*ode 62

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

str = string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
Run Code Online (Sandbox Code Playgroud)

在带有空格的简单字符串上进行10,000循环的计时包含新行和制表符

  • split/join = 60毫秒
  • linq chararray = 94毫秒
  • 正则表达式= 437毫秒

通过将其包装在方法中来改善它,使其具有意义,并且在我们处理它时也使它成为一种扩展方法......

public static string RemoveWhitespace(this string str) {
    return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
}
Run Code Online (Sandbox Code Playgroud)

  • 很酷的想法...但我会这样做:`string.Concat("H \ne llo Wor ld".Split())` (6认同)
  • @kernowcode你的意思是2个重载与`string []`和`char []`之间的歧义?你只需要指定你想要的那个,例如:`string.Join("",str.Split((string [])null,StringSplitOptions.RemoveEmptyEntries));`.这实际上是你在这种情况下对`default`的调用,因为它也返回`null`:它帮助编译器决定选择哪个重载.因此我的评论是因为您的评论中的语句"Split需要一个有效的数组而null将不会..."是错误的.没什么大不了的,只是因为杰克德鲁问起这是如何起作用的.+1为你的答案 (4认同)
  • michaelkrisper解决方案非常易读.我做了一个测试,'split/join'(162毫秒)比同一个字符串的10,000次迭代的'split/concat'(180毫秒)表现更好. (3认同)
  • 我真的很喜欢这个解决方案,自从LINQ之前的日子以来,我一直在使用类似的解决方案.我对LINQs的性能印象深刻,并且对正则表达式感到有些惊讶.也许代码不像正则表达式那样最优(例如,你必须缓存正则表达式对象).但问题的关键在于数据的"质量"会很重要.也许有很长的字符串,正则表达式将胜过其他选项.这将是一个有趣的基准... ...--) (2认同)
  • default(string[]) == 所有空白字符的列表如何?我看到它工作,但我不明白如何? (2认同)

Sti*_*ahl 35

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

长输入字符串结果:

  1. InPlaceCharArray:2021 ms(Sunsetquest的回答) - (原文出处)
  2. 字符串阅读器:6082毫秒
  3. LINQ使用本机char.IsWhitespace:7357毫秒
  4. LINQ:7746 ms(Henk的回答)
  5. ForLoop:32320毫秒
  6. 正则表达式编译:37157毫秒
  7. 正则表达式:42940毫秒

短输入字符串结果:

  1. InPlaceCharArray:108毫秒(Sunsetquest的回答) - (原始来源)
  2. 字符串阅读器:327毫秒
  3. ForLoop:343毫秒
  4. LINQ使用本机char.IsWhitespace:624毫秒
  5. LINQ:645ms(Henk的回答)
  6. 正则表达式编译:1671毫秒
  7. 正则表达式: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);
    }
}
Run Code Online (Sandbox Code Playgroud)

测试:

[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);
    }
}
Run Code Online (Sandbox Code Playgroud)


Blu*_*ppy 23

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

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

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

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

  • 可能是真的 - 但答案仍然存在,可读性,比正则表达式更快,并产生所需的结果.许多其他答案都在此之后...因此,downvote没有意义. (6认同)
  • 因为它可以编写得更短:new string(input.Where(c =>!Char.IsWhiteSpace(c)).ToArray()); (3认同)
  • 为什么这被否决了?它完全可以接受,满足要求,比 RegEx 选项运行得更快并且可读性很强? (2认同)
  • 是否有“ 0.82”的单位?还是相对衡量(82%)?您可以编辑答案以使其更清晰吗? (2认同)

Sun*_*est 17

我在Felipe Machado的CodeProject 找到了一篇很好的文章(在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));
}
Run Code Online (Sandbox Code Playgroud)

安全的版本......

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);
}
Run Code Online (Sandbox Code Playgroud)

Stian Standahl的Stack Overflow 还有一些不错的独立基准测试,它们也展示了Felipe的功能如何比下一个最快的功能快300%.

  • 我无法抗拒。查看您所引用的文章的评论部分。您会发现我是“ Basketcase Software”。他和他一起工作了一段时间。当这个问题再次出现时,我完全忘记了这一点。感谢您的美好回忆。:) (2认同)

小智 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);
    }
Run Code Online (Sandbox Code Playgroud)


Mak*_*ood 8

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

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


lar*_*mil 6

我想很多人来这里就是为了删除空格。:

string s = "my string is nice";
s = s.Replace(" ", "");
Run Code Online (Sandbox Code Playgroud)

  • 它的 s.Replace() (2认同)