Эффективный способ удалить все пробелы из строки?

360

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

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

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

Кори Огберн
источник
6
Разбор XML с помощью регулярных выражений почти так же плох, как и анализ HTML с помощью регулярных выражений .
ДТБ
3
@henk holterman; Смотрите мой ответ ниже, регулярное выражение не кажется самым быстрым во всех случаях.
Henk J Meulekamp
Регекс не кажется самым быстрым на всех. Я суммировал результаты по множеству различных способов удаления пробелов из строки. Сводка в ответе ниже - stackoverflow.com/a/37347881/582061
Стиан Штэндаль

Ответы:

617

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

Regex.Replace(XML, @"\s+", "")
slandau
источник
1
Я мог бы использовать регулярное выражение, я просто не уверен, что это самый быстрый способ.
Кори Огберн
1
Я уверен, что это так. По крайней мере, за кулисами вы должны проверить каждого персонажа, а это просто выполнить линейный поиск.
slandau
19
Не должно ли это быть Regex.Replace(XML, @"\s+", "")?
Январь-Петр Вос
61
Если вы планируете делать это более одного раза, создайте и сохраните экземпляр Regex. Это позволит сэкономить на его создании каждый раз, что дороже, чем вы думаете. private static readonly Regex sWhitespace = new Regex(@"\s+"); public static string ReplaceWhitespace(string input, string replacement) { return sWhitespace.Replace(input, replacement); }
Hypehuman
10
Для новичков в RegEx и поиска объяснения того, что означает это выражение, \sозначает «соответствовать любому пустому токену» и +означает «соответствовать одному или нескольким исходящим токенам». Кроме того, RegExr - хороший сайт для практики написания выражений RegEx, если вы хотите поэкспериментировать.
JRH
181

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

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

Henk J Meulekamp
источник
40
.ToCharArray()не обязательно; Вы можете использовать .Where()непосредственно на строку.
ProgramFOX
10
Просто отметить здесь. Regex медленнее ... на маленьких строках! Если вы говорите, что у вас была оцифрованная версия тома по налоговому законодательству США (~ миллион слов?) С несколькими итерациями, Regex - король, безусловно! Не то, что быстрее, а то, что следует использовать при каких обстоятельствах. Вы доказали здесь только половину уравнения. -1 до тех пор, пока вы не докажете вторую половину теста, чтобы в ответе было больше информации о том, когда что следует использовать.
Петр Кула
17
@ppumkin Он попросил удалить пробел за один проход. Не многократные итерации другой обработки. Я не собираюсь превращать это однопроходное удаление пробелов в расширенный пост о тестировании обработки текста.
Хенк Дж Meulekamp
1
Вы сказали, что на этот раз предпочитаете не использовать регулярные выражения, но не сказали почему.
Петр Кула
2
@ProgramFOX, в другом вопросе (не могу его легко найти) я заметил, что по крайней мере в некоторых запросах использование ToCharArrayбыстрее, чем использование .Where()непосредственно в строке. Это как-то связано с накладными расходами IEnumerable<>в каждом шаге итерации, а ToCharArrayих эффективность (block-copy) и компилятор оптимизирует итерацию по массивам. Почему эта разница существует, никто не смог объяснить мне, но измерить, прежде чем удалить ToCharArray().
Авель
87

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

XML.Replace(" ", string.Empty);
mike_k
источник
28
Не удаляет вкладки или новые строки. Если я делаю несколько удалений сейчас, я делаю несколько проходов через строку.
Кори Огберн
11
Даунвот за то, что не убрал все пробелы, как это делают ответы Сландау и Хенка.
Мэтт Сах
@MattSach, почему он не удаляет ВСЕ пробелы?
Zapnologica
4
@Zapnologica Это только замена пробелов. ОП также попросил заменить символы новой строки (которые являются символами «пробела», хотя они не являются пробелами).
Мэтт Сач
76

Мое решение состоит в том, чтобы использовать 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));
}
kernowcode
источник
3
Мне очень нравится это решение, я использую подобное еще до дней LINQ. Я действительно впечатлен производительностью LINQ и несколько удивлен регулярным выражением. Возможно, код был не таким оптимальным, как это могло бы быть для регулярных выражений (например, вам придется кэшировать объект регулярного выражения). Но суть проблемы в том, что «качество» данных будет иметь большое значение. Возможно, с длинными строками регулярное выражение превзойдет другие параметры. Это будет забавный эталон для выступления ... :-)
Loudenvier
1
Как по умолчанию (строка []) == список всех пробельных символов? Я вижу это работает, но я не понимаю, как?
Джейк Дрю
5
@kernowcode Вы имеете в виду неоднозначность между двумя перегрузками с string[]и char[]? Вы просто должны указать , какой из них вы хотите , например: string.Join("", str.Split((string[])null, StringSplitOptions.RemoveEmptyEntries));. Это именно то, что делает ваш вызов defaultв этом случае, так как он также возвращает null: он помогает компилятору решить, какую перегрузку выбрать. Отсюда мой комментарий, потому что утверждение в вашем комментарии "Split нужен правильный массив, и null не будет делать ..." является ложным. Ничего страшного, просто стоит упомянуть, поскольку Джейк Дрю спросил, как это работает. +1 за ваш ответ
Фрэнк Дж
6
Классная идея ... но я бы сделал это следующим образом:string.Concat("H \ne llo Wor ld".Split())
michaelkrisper
3
Решение michaelkrisper очень читабельно. Я выполнил тест и «split / join» (162 миллисекунды) работал лучше, чем «split / concat» (180 миллисекунд) для 10000 итераций одной и той же строки.
Kernowcode
45

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

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

  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мс ( ответ Хенка )
  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.

Стиан Стандаль
источник
24

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

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

BlueChippy
источник
1
Почему это было отвергнуто? Это вполне приемлемо, соответствует требованиям, работает быстрее, чем опция RegEx, и хорошо читается?
BlueChippy
4
потому что это может быть написано намного короче: новая строка (input.Where (c =>! Char.IsWhiteSpace (c)). ToArray ());
Бас Смит
7
Может быть и правдой - но ответ все еще остается, читается, быстрее, чем регулярное выражение и дает желаемый результат. Многие другие ответы ПОСЛЕ этого ... поэтому понижение не имеет смысла.
BlueChippy
2
Есть ли единица для "0,82"? Или это относительная мера (82%)? Можете ли вы отредактировать свой ответ, чтобы сделать его более понятным?
Питер Мортенсен
20

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

Sunsetquest
источник
Я пытался перевести это на C ++, но я немного застрял. Есть идеи, почему мой порт не работает? stackoverflow.com/questions/42135922/…
Джон Кейдж
2
Я не могу сопротивляться. Посмотрите в разделе комментариев статьи, на которую вы ссылаетесь. Вы найдете меня как «Программное обеспечение Basketcase». Он и работал над этим вместе некоторое время. Я полностью забыл об этом, когда эта проблема снова возникла. Спасибо за хорошие воспоминания. :)
Ричард Робертсон
1
А что, если вы хотите удалить только дополнительный WS? А как насчет этого мода stackoverflow.com/questions/17770202/… ?
Том
Быстрее, немного медленнее ;-) Строка, так как контейнер работает лучше (в приложении с 4:15 до 3:55 => 8,5% меньше, но если оставить строку 3:30 => 21,4% меньше, и профиллер показывает, что около 50% потрачено в Этот способ). Таким образом, в реальном времени строка должна быть примерно на 40% быстрее по сравнению с (медленным) преобразованием массива, используемым здесь.
Том
15

Если вам нужна превосходная производительность, в этом случае следует избегать 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);
    }
JHM
источник
Мне было бы любопытно узнать подробности ваших сравнительных тестов - не то, чтобы я скептически относился, но мне любопытно, какие накладные расходы связаны с Linq. Насколько это было плохо?
Марк Мейер
Я не запускал все тесты повторно, но я помню это очень много: все, что включало Linq, было намного медленнее, чем что-либо без него. Все умное использование строковых / char-функций и конструкторов не имело никакой разницы в процентах, если использовался Linq.
JHM
11

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

public static partial class Extension
{
    public static string RemoveWhiteSpace(this string self)
    {
        return new string(self.Where(c => !Char.IsWhiteSpace(c)).ToArray());
    }
}
Maksood
источник
это, по сути, ненужный ответ (регулярное выражение является излишним, но это более быстрое решение, чем данное - и оно уже принято?)
W1ll1amvl
Как вы можете использовать методы расширения Linq для строки? Не могу понять, с помощью чего я System.Linq
скучаю,
Хорошо, похоже, что это не доступно в PCL, IEnumerable <char> является условным в реализации Microsoft String ... И я использую Profile259, который не поддерживает это :)
GGirard
4

Вот простая линейная альтернатива решению 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();
}
Брэндон Моретц
источник
3

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

"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();
}
user1325543
источник
2

Я предполагаю, что ваш 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");
DTB
источник
Как только я проверяю, что определенный тег <name> имеет правильное значение, я готов. Разве при разборе документа не будет каких-то накладных расходов?
Кори Огберн
4
Конечно, у него есть некоторые накладные расходы. Но преимущество в том, что он правильный. Решение, основанное, например, на регулярном выражении, гораздо сложнее понять правильно. Если вы решите, что решение LINQ to XML слишком медленное, вы всегда можете заменить его чем-то более быстрым. Но вам следует избегать поиска наиболее эффективной реализации, прежде чем вы поймете, что правильная реализация слишком медленная.
ДТБ
Это будет работать на внутренних серверах моего работодателя. Легкий вес это то, что я ищу. Я не хочу что-то, что «просто работает», но оптимально.
Кори Огберн
4
LINQ to XML - один из самых легких способов правильной работы с XML в .NET
dtb
1

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

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

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

Фред
источник
1

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

    public static string RemoveWhitespace(this string input)
    {
        if (input == null)
            return null;
        return new string(input.ToCharArray()
            .Where(c => !Char.IsWhiteSpace(c))
            .ToArray());
    }
Тарик БЕНАРАБ
источник
Это почти точно так же, как ответ Хенка выше. Единственная разница в том, что вы проверяете null.
Кори Огберн
Да, проверка на ноль
важно
1
Может быть, это просто комментарий к его ответу. Я рад, что ты поднял это хотя. Я не знал, что методы расширения могут быть вызваны для нулевых объектов.
Кори Огберн
0

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

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 минут.
hvanbrug
источник