一个快速的脑筋急转弯:给一个字符串
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;
}
}
答案 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]+", " ");
<强>更新强>:
Hasan和Ani都提出了在满足“使用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);
}
}