Так как никто, кажется, не дал удовлетворительного ответа, я придумал один. Вот строковое решение (.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")
) было в 3,5 раза медленнее , чем моя версия LINQ (string.Join("", s.RemoveRepeatedElements())
).
Я также попробовал «идеальное» процедурное решение:
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 раз быстрее, чем регулярное выражение!