我可以使用LINQ去除字符串中的重复空格吗?

时间:2010-08-29 16:37:02

标签: c# linq linq-to-objects

一个快速的脑筋急转弯:给一个字符串

This  is a string with  repeating   spaces

LINQ表达的最终结果是

This is a string with repeating spaces

谢谢!

供参考,这是一种非LINQ方式:

private static IEnumerable<char> RemoveRepeatingSpaces(IEnumerable<char> text)
{
  bool isSpace = false;
  foreach (var c in text)
  {
    if (isSpace && char.IsWhiteSpace(c)) continue;

    isSpace = char.IsWhiteSpace(c);
    yield return c;
  }
}

7 个答案:

答案 0 :(得分:14)

这不是linq类型的任务,请使用正则表达式

string output = Regex.Replace(input," +"," ");

当然你可以使用linq将它应用于字符串集合。

答案 1 :(得分:6)

public static string TrimInternal(this string text)
{
  var trimmed = text.Where((c, index) => !char.IsWhiteSpace(c) || (index != 0 && !char.IsWhiteSpace(text[index - 1])));
  return new string(trimmed.ToArray());
}

答案 2 :(得分:3)

由于似乎没有人给出满意的答案,我想出了一个。这是一个基于字符串的解决方案(.Net 4):

public static string RemoveRepeatedSpaces(this string s)
{
    return s[0] + string.Join("",
           s.Zip(
               s.Skip(1),
               (x, y) => x == y && y == ' ' ? (char?)null : y));
}

然而,这只是从序列中删除重复元素的一般情况,所以这里是通用版本:

public static IEnumerable<T> RemoveRepeatedElements<T>(
                             this IEnumerable<T> s, T dup)
{
    return s.Take(1).Concat(
            s.Zip(
                s.Skip(1),
                (x, y) => x.Equals(y) && y.Equals(dup) ? (object)null : y)
            .OfType<T>());
}

当然,这只是一个更具体的函数版本,可以从输入流中删除所有连续的重复项:

public static IEnumerable<T> RemoveRepeatedElements<T>(this IEnumerable<T> s)
{
    return s.Take(1).Concat(
            s.Zip(
                s.Skip(1),
                (x, y) => x.Equals(y) ? (object)null : y)
            .OfType<T>());
}

显然你可以用第二个函数来实现第一个函数:

public static string RemoveRepeatedSpaces(this string s)
{
    return string.Join("", s.RemoveRepeatedElements(' '));
}

顺便说一下,我将我的最后一个函数与正则表达式版本(Regex.Replace(s, " +", " "))进行了基准测试,它们之间的差距在几纳秒之内,因此与额外的正则表达式开销相比,额外的LINQ开销可以忽略不计。当我将其概括为删除所有连续的重复字符时,等效的正则表达式(Regex.Replace(s, "(.)\\1+", "$1")比我的LINQ版本(string.Join("", s.RemoveRepeatedElements())慢3.5倍。

我也尝试了“理想的”程序解决方案:

public static string RemoveRepeatedSpaces(string s)
{
    StringBuilder sb = new StringBuilder(s.Length);
    char lastChar = '\0';
    foreach (char c in s)
        if (c != ' ' || lastChar != ' ')
            sb.Append(lastChar = c);
    return sb.ToString();
}

这比正则表达式快5倍!

答案 3 :(得分:2)

在实践中,我可能只是使用您的原始解决方案或正则表达式(如果您想要一个快速和简单的解决方案)。使用lambda函数的一种令人讨厌的方法是定义一个固定点运算符:

T FixPoint<T>(T initial, Func<T, T> f) {
   T current = initial;
   do { 
     initial = current;
     current = f(initial);
   } while (initial != current);
   return current;
}

这会一直重复调用操作f,直到操作返回与参数相同的值。您可以将操作视为一个通用循环 - 它非常有用,但我猜它太过于令人讨厌,无法包含在.NET BCL中。然后你可以写:

string res = FixPoint(original, s => s.Replace("  ", " "));

它没有原始版本那么高效,但除非有太多空格,否则它应该可以正常工作。

答案 4 :(得分:0)

Paul Creasey's answer是要走的路。

如果您想将制表符视为空白,请使用:

text = Regex.Replace(text, "[ |\t]+", " ");

<强>更新

HasanAni都提出了在满足“使用LINQ”要求的同时解决此问题的最合理方法。但请注意,这些解决方案涉及通过索引访问字符串中的字符。

LINQ方法的精神在于它可以应用于任何可枚举的序列。因为任何合理有效的解决方案都需要保持某种状态(使用Ani和Hasan的解决方案很容易错过这个事实,因为状态已经在字符串本身内维护),接受任何项目序列的通用方法很可能会使用过程代码实现更直接。

然后,当然,这个程序代码可以抽象看起来的方法中,就像LINQ风格的方法一样。但我不建议从一开始就以“我想在此解决方案中使用LINQ”的态度解决这样的问题,因为它会对您的代码施加非常尴尬的限制。

对于它的价值,这是我如何实现一般的想法。

public static IEnumerable<T> StripConsecutives<T>(this IEnumerable<T> source, T value, IEqualityComparer<T> comparer)
{
    // null-checking omitted for brevity

    using (var enumerator = source.GetEnumerator())
    {
        if (enumerator.MoveNext())
        {
            yield return enumerator.Current;
        }
        else
        {
            yield break;
        }

        T prev = enumerator.Current;
        while (enumerator.MoveNext())
        {
            T current = enumerator.Current;
            if (comparer.Equals(prev, value) && comparer.Equals(current, value))
            {
                // This is a consecutive occurrence of value --
                // moving on...
            }
            else
            {
                yield return current;
            }
            prev = current;
        }
    }
}

答案 5 :(得分:0)

Linq根据定义与可枚举(即集合,列表,数组)相关。你可以将你的字符串转换为char的集合并选择非空格,但这肯定不是Linq的工作。

答案 6 :(得分:0)

拆分为列表,过滤,然后重新加入,2 行代码...

var test = "  Alpha      Beta     Tango    ";

var l = test.Split(' ').Where(s => !string.IsNullOrEmpty(s));
var result = string.Join(" ", l);

// result = "Alpha Beta Tango"

重构为扩展方法:

using Extensions;
void Main()
{
    var test = " Alpha    Beta   Tango    ";
    var result = test.RemoveRepeatedSpaces();
    // result = "Alpha Beta Tango";
}

static class Extentions 
{
    public static string RemoveRepeatedSpaces(this string s)
    {
        if (s == null) 
           return string.Empty;

        var l = s.Split(' ').Where(a => !string.IsNullOrEmpty(a));
        return string.Join(" ", l);
    }
}