Эффективный способ удаления ВСЕХ пробелов из строки?

Я вызываю REST API и получаю ответ XML. Он возвращает список имен рабочей области, и я пишу быстрый метод IsExistingWorkspace(). Поскольку все рабочие пространства состоят из смежных символов без пробелов, я предполагаю, что самый простой способ выяснить, есть ли конкретное рабочее пространство в списке, - удалить все пробелы (включая новые строки) и сделать это (XML - это строка, полученная из Интернета запрос):

XML.Contains("<name>" + workspaceName + "</name>");

Я знаю, что он чувствителен к регистру, и я полагаюсь на это. Мне просто нужен способ эффективно удалить все пробелы в строке. Я знаю, что RegEx и LINQ могут это сделать, но я открыт для других идей. Меня больше всего беспокоит скорость.

Ответ 1

Это самый быстрый способ, о котором я знаю, хотя вы сказали, что не хотите использовать регулярные выражения:

Regex.Replace(XML, @"\s+", "")

Ответ 2

У меня есть альтернативный способ без regexp, и, похоже, он работает очень хорошо. Это продолжение ответа Брэндона Моретца:

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

Я протестировал его в простой unit test:

[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 секунды.

Ответ 3

Попробуйте метод замены строки в С#.

XML.Replace(" ", string.Empty);

Ответ 4

Мое решение - использовать Split и Join, и это удивительно быстро, на самом деле самый быстрый из лучших ответов здесь.

str = string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));

Сроки для 10000 циклов на простой строке с пробелом в новых строках и вкладках

  • split/join = 60 миллисекунд
  • linq chararray = 94 миллисекунды
  • regex = 437 миллисекунд

Улучшите это, завернув его в метод, чтобы придать ему значение, а также сделайте его методом расширения, пока мы на нем...

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

Ответ 5

Опираясь на ответ Хенкса, я создал несколько тестовых методов с его ответом и несколько добавленных, более оптимизированных методов. Я обнаружил, что результаты отличаются в зависимости от размера входной строки. Поэтому я проверил с двумя наборами результатов. В самом быстром способе связанный источник имеет еще более быстрый путь. Но, поскольку это характеризуется как небезопасный, я оставил это.

Длинная строка входных результатов:

  1. InPlaceCharArray: 2021 мс (ответ Sunsetquest) - (Оригинальный источник)
  2. Разделение строки, затем присоединение: 4277мс (ответ Kernowcode)
  3. Считыватель строк: 6082 мс
  4. LINQ с использованием собственного символа char. IsWhitespace: 7357 мс
  5. LINQ: 7746 мс (ответ Хенка)
  6. ForLoop: 32320 мс
  7. RegexCompiled: 37157 мс
  8. Регулярное выражение: 42940 мс

Короткие результаты ввода строки:

  1. InPlaceCharArray: 108 мс (ответ Sunsetquest) - (оригинальный источник)
  2. Разделение строки, затем присоединение: 294 мс (ответ Kernowcode)
  3. Считыватель строк: 327 мс
  4. ForLoop: 343 мс
  5. LINQ с использованием собственного символа char. IsWhitespace: 624 мс
  6. LINQ: 645мс (ответ Henk)
  7. RegexCompiled: 1671 мс
  8. Регулярное выражение: 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);
    }
}

Тесты:

[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);
    }
}

Редактировать: Протестирован хороший лайнер от Kernowcode.

Ответ 6

Просто альтернатива, потому что это выглядит довольно хорошо:) - ПРИМЕЧАНИЕ: Ответ Henks является самым быстрым из них.

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 секунды
Regex = 2.58 секунд
new String (Хенкс) = 0,82

Ответ 7

Я нашел хорошую статью об этом в 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% быстрее, чем следующая самая быстрая функция.

Ответ 8

Если вам нужна превосходная производительность, вы должны избегать 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);
    }

Ответ 9

Регулярное выражение избыточное; просто используйте расширение на строке (спасибо Henk). Это тривиально и должно быть частью структуры. Во всяком случае, здесь моя реализация:

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

Ответ 10

Вот простая линейная альтернатива решению 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();
}

Ответ 11

Мне нужно было заменить пробел в строке пробелами, но не дублировать пробелы. например, мне нужно было преобразовать что-то вроде следующего:

"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();
}

Ответ 12

Я предполагаю, что ваш ответ 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");

Ответ 13

Вот еще один вариант:

public static string RemoveAllWhitespace(string aString)
{
  return String.Join(String.Empty, aString.Where(aChar => aChar !Char.IsWhiteSpace(aChar)));
}

Как и в большинстве других решений, я не выполнял исчерпывающие тесты тестов, но это работает достаточно хорошо для моих целей.

Ответ 14

Я нашел разные результаты, чтобы быть правдой. Я пытаюсь заменить все пробелы на одно пространство, и регулярное выражение было очень медленным.

return( Regex::Replace( text, L"\s+", L" " ) );

Что наиболее оптимально работало для меня (в С++ 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 );
}

Сначала я попытался выполнить описанную выше процедуру, заменив каждый символ отдельно, но мне пришлось переключиться на выполнение подстрок для не-пространственных разделов. При применении к строке в 1 200 000 символов:

  • описанная выше процедура делает это через 25 секунд
  • вышеуказанная процедура + замена отдельного символа за 95 секунд
  • регулярное выражение прерывается через 15 минут.

Ответ 15

Мы можем использовать:

    public static string RemoveWhitespace(this string input)
    {
        if (input == null)
            return null;
        return new string(input.ToCharArray()
            .Where(c => !Char.IsWhiteSpace(c))
            .ToArray());
    }

Ответ 16

Скажем, у нас есть эта строка: string MyString = " test test test". Вы можете разделить строку с одним разделителем пробелов: MyString.Split(' '). В результате получается массив пустых строк и других чартеров. В этом случае он генерирует этот массив: { "", "test", "test", "", "", "", "test" }. Затем, используя метод string.concat, вы можете объединить все строки в массиве, что приводит к "testtesttest".

string MyString = " test test    test";
MyString = string.Concat(MyString.Split(' '));

Ответ 17

Мы можем использовать System.Linq, и мы можем сделать это в одной строке:

string text = "My text with white spaces...";
text = new string(text.ToList().Where(c => c != ' ').ToArray());

Ответ 18

String s = Console.ReadLine();
s = s.Replace(" ", String.Empty);
String[] arr = s.Split(' ');
foreach(char num in s)
{
     Console.WriteLine(num);
}

Этот блок кода удаляет все пробелы из строки.