Эффективный способ удалить все пробелы из строки?
Я вызываю REST API и получаю ответ XML обратно. Он возвращает список имен рабочей области, и я пишу быстрый IsExistingWorkspace()
метод. Поскольку все рабочие пространства состоят из смежных символов без пробелов, я предполагаю, что самый простой способ выяснить, находится ли конкретное рабочее пространство в списке, это удалить все пробелы (включая переводы строк) и сделать это (XML - это строка, полученная из Интернета. запрос):
XML.Contains("<name>" + workspaceName + "</name>");
Я знаю, что это с учетом регистра, и я полагаюсь на это. Мне просто нужен способ, чтобы эффективно удалить все пробелы в строке. Я знаю, что RegEx и LINQ могут это сделать, но я открыт для других идей. Меня больше всего беспокоит скорость.
21 ответ
Это самый быстрый способ, который я знаю, даже если вы сказали, что не хотите использовать регулярные выражения:
Regex.Replace(XML, @"\s+", "")
У меня есть альтернативный способ без регулярных выражений, и, похоже, он работает довольно хорошо. Это продолжение ответа Брэндона Морец:
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 попыток первый вариант (без регулярного выражения) выполняется менее чем за секунду (700 мс на моем компьютере), а второй занимает 3,5 секунды.
Попробуйте заменить метод строки в C#.
XML.Replace(" ", string.Empty);
Мое решение состоит в том, чтобы использовать Split и Join, и это удивительно быстро, на самом деле самый быстрый из лучших ответов здесь.
str = string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
Время цикла 10000 на простой строке с пробелами, включая новые строки и вкладки
- split/join = 60 миллисекунд
- linq chararray = 94 миллисекунды
- регулярное выражение = 437 миллисекунд
Улучшите это, обернув его в метод, чтобы придать ему смысл, а также сделайте его методом расширения, пока мы на нем...
public static string RemoveWhitespace(this string str) {
return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
}
Опираясь на ответ Хенкса, я создал несколько тестовых методов с его ответом и несколько добавленных, более оптимизированных, методов. Я обнаружил, что результаты отличаются в зависимости от размера входной строки. Поэтому я проверил с двумя наборами результатов. В самом быстром способе связанный источник имеет еще более быстрый путь. Но, поскольку это характеризуется как небезопасный, я оставил это.
Длинная строка входных результатов:
- InPlaceCharArray: 2021 мс ( ответ Sunsetquest) - ( Первоисточник)
- Считыватель строк: 6082 мс
- LINQ с использованием собственного символа char.IsWhitespace: 7357 мс
- LINQ: 7746 мс ( ответ Хенка)
- ForLoop: 32320 мс
- RegexCompiled: 37157 мс
- Регулярное выражение: 42940 мс
Короткие результаты ввода строки:
- InPlaceCharArray: 108 мс ( ответ Sunsetquest) - ( Первоисточник)
- Считыватель строк: 327 мс
- ForLoop: 343 мс
- LINQ с использованием собственного символа char.IsWhitespace: 624 мс
- LINQ: 645мс ( ответ Хенка)
- RegexCompiled: 1671 мс
- Регулярное выражение: 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 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);
}
}
Просто альтернатива, потому что это выглядит довольно красиво:) - ПРИМЕЧАНИЕ: ответ Хенкса - самый быстрый из них.
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
(Хенкс) = 0,82
Я нашел хорошую статью об этом в CodeProject от Фелипе Мачадо (с помощью Ричарда Робертсона)
Он проверил десять разных методов. Это самая быстрая небезопасная версия...
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));
}
И самая быстрая безопасная версия...
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);
}
Есть также несколько хороших независимых тестов переполнения стека от Stian Standahl, которые также показывают, что функция Felipe примерно на 300% быстрее, чем следующая самая быстрая функция.
Если вам нужна превосходная производительность, в этом случае следует избегать 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);
}
Регулярное выражение является излишним; просто используйте расширение на строку (спасибо Хенк). Это тривиально и должно было быть частью структуры. Во всяком случае, вот моя реализация:
public static partial class Extension
{
public static string RemoveWhiteSpace(this string self)
{
return new string(self.Where(c => !Char.IsWhiteSpace(c)).ToArray());
}
}
Я думаю, что многие люди приходят сюда за удалением пробелов. :
string s = "my string is nice";
s = s.replace(" ", "");
Вот простая линейная альтернатива решению 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();
}
Мне нужно было заменить пробел в строке пробелами, но не дублировать пробелы. например, мне нужно было преобразовать что-то вроде следующего:
"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();
}
Мы можем использовать:
public static string RemoveWhitespace(this string input)
{
if (input == null)
return null;
return new string(input.ToCharArray()
.Where(c => !Char.IsWhiteSpace(c))
.ToArray());
}
Я предполагаю, что ваш 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");
Используя Linq, вы можете написать читаемый метод следующим образом:
public static string RemoveAllWhitespaces(this string source)
{
return string.IsNullOrEmpty(source) ? source : new string(source.Where(x => !char.IsWhiteSpace(x)).ToArray());
}
Вот еще один вариант:
public static string RemoveAllWhitespace(string aString)
{
return String.Join(String.Empty, aString.Where(aChar => aChar !Char.IsWhiteSpace(aChar)));
}
Как и с большинством других решений, я не проводил исчерпывающих тестов производительности, но для моих целей это работает достаточно хорошо.
Простой способ удалить все пробелы из строки, "пример" - это ваша начальная строка.
String.Concat(example.Where(c => !Char.IsWhiteSpace(c))
Возможно, это не так выразительно, как использование Regex или использование , но следующая версия, безусловно, является наиболее краткой:
public static string RemoveWhitespace(this string input)
{
return string.Concat(input.Split(null));
}
Это позволяет использовать
Split(Char[])
перегрузка
Split()
, который принимает в качестве единственного параметра и интерпретирует это значение как «разбиение на все пробелы» (тот же результат, как если бы вы использовали пустойchar
массив илиdefault(char[])
вместо).
Внутри он использует
Char.IsWhiteSpace
чтобы определить, следует ли разделять его по данному символу:
Если
separator
аргумент этоnull
или не содержит символов, метод рассматривает символы пробела как разделители. Символы пробелов определяются стандартом Unicode, а метод Char.IsWhiteSpace возвращаетtrue
если ему передан символ пробела.
Я нашел разные результаты, чтобы быть правдой. Я пытаюсь заменить все пробелы одним пробелом, и регулярное выражение было очень медленным.
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 );
}
Сначала я попробовал описанную выше процедуру, заменив каждый символ отдельно, но мне пришлось переключиться на выполнение подстрок для непробельных разделов. При применении к строке 120000 символов:
- вышеупомянутая процедура делает это за 25 секунд
- вышеуказанная процедура + отдельная замена персонажа за 95 секунд
- регулярное выражение прерывается через 15 минут.
Мы можем использовать System.Linq, и мы можем сделать это в одной строке:
string text = "My text with white spaces...";
text = new string(text.ToList().Where(c => c != ' ').ToArray());
String s = Console.ReadLine();
s = s.Replace(" ", String.Empty);
String[] arr = s.Split(' ');
foreach(char num in s)
{
Console.WriteLine(num);
}
Этот блок кода удаляет все пробелы из строки.