Ефективний спосіб видалити ВСІ пробіли з рядка?


358

Я дзвоню API REST і отримую відповідь XML. Він повертає список назв робочої області, і я пишу швидкий IsExistingWorkspace()метод. Оскільки всі робочі простори складаються з суміжних символів без пробілів, я вважаю, що найпростішим способом з’ясувати, чи є певна робоча область у списку, є видалити всі пробіли (включаючи нові рядки) і зробити це (XML - рядок, отриманий з Інтернету запит):

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

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


6
Розбір XML за допомогою regex майже такий же поганий, як і аналіз HTML з regex .
dtb

3
@henk holterman; Дивіться мою відповідь нижче, regexp здається не найшвидшим у всіх випадках.
Henk J Meulekamp

Regex, здається, зовсім не найшвидший. Я узагальнив результати багатьох способів видалення пробілів з рядка. Резюме в відповідь нижче - stackoverflow.com/a/37347881/582061
Stian Standahl

Відповіді:


616

Це найшвидший спосіб, про який я знаю, навіть якщо ви сказали, що не хочете використовувати регулярні вирази:

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

1
Я міг би використовувати регулярний вираз, я просто не впевнений, чи це найшвидший шлях.
Corey Ogburn

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); }
гіпегуман

10
Для тих, хто є новим у RegEx та шукає пояснення, що означає це вираз, \sозначає "відповідати будь-якому маркеру пробілу" та +означає "відповідати одному чи декільком маркерів, що надходять". Також RegExr є приємним веб-сайтом для практику писати вирази RegEx, якщо ви хочете експериментувати.
jrh

181

У мене є альтернативний спосіб без regexp, і він, здається, працює досить добре. Це продовження відповіді Брендона Мореца:

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


40
.ToCharArray()не потрібно; ви можете використовувати .Where()безпосередньо на рядку.
ProgramFOX

10
Просто зазначити тут. Regex повільніше ... на невеликих струнах! Якщо ви скажете, що у вас була оцифрована версія Тома про податкове законодавство США (~ мільйон слів?), З кількома ітераціями, Regex є королем! Справа не в тому, що швидше, а в тому, що слід використовувати за яких обставин. Ви лише довели тут половину рівняння. -1, поки ви не доведете другу половину тесту, щоб відповідь дала більше розуміння того, що слід використовувати.
Пьотр Кула

17
@ppumkin Він попросив видалити пробіл за один прохід. Не множинні ітерації іншої обробки. Я не збираюся робити це єдине видалення пробілів у розширений пост про тестування обробки тексту.
Henk J Meulekamp

1
Ви сказали, що цього разу не хочете використовувати для регулярного вирівнювання, але не сказали, чому.
Пьотр Кула

2
@ProgramFOX, в іншому запитанні (не можу його легко знайти) я помітив, що принаймні в деяких запитах використання ToCharArrayшвидше, ніж використання .Where()безпосередньо в рядку. Це має щось спільне з накладними витратами на IEnumerable<>кожному етапі ітерації, а ToCharArrayістота дуже ефективна (блок-копія) та компілятор оптимізує ітерацію над масивами. Чому така різниця існує, ніхто мені не зміг пояснити, але заміряйте, перш ніж ви їх усунете ToCharArray().
Абель

87

Спробуйте замінити рядок на C #.

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

28
Не видаляє вкладки чи нові рядки. Якщо зараз я роблю кілька видалень, я роблю декілька проходів по рядку.
Corey Ogburn

11
Захист для того, щоб не видаляти весь пробіл, як це робить відповідь Slandau та Henk.
Метт Сах

@MattSach чому він не видаляє ВСІ пробіли?
Zapnologica

4
@Zapnologica Це лише заміна символів пробілу. ОП попросила також замінити нові рядки (які є "пробілами" символів, навіть якщо вони не є пробілом).
Метт Сах

75

Моє рішення - використовувати Split та Join, і це напрочуд швидко, насправді найшвидший з найкращих відповідей тут.

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

Часи для 10 000 циклу на простому рядку з пробілом з новими лініями та вкладками

  • розділити / з'єднати = 60 мілісекунд
  • linkq chararray = 94 мілісекунди
  • регулярний вираз = 437 мілісекунд

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

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

3
Мені дуже подобається це рішення, я використовую подібне ще з днів до LINQ. Я насправді вражений роботою LINQ і дещо здивований регексом. Можливо, код був не настільки оптимальним, як це міг би бути для регулярного виведення (вам доведеться кешувати об'єкт регулярного вираження, наприклад). Але суть проблеми полягає в тому, що "якість" даних буде мати велике значення. Можливо, з довгими рядками регулярний вираз перевершить інші варіанти. Це буде веселий орієнтир для виконання ... :-)
Loudenvier

1
Як за замовчуванням (рядок []) == перелік усіх символів пробілу? Я бачу, як це працює, але я не розумію, як?
Джейк Дрю

5
@kernowcode Ви маєте в виду неоднозначність між 2 перевантаженнями з string[]і char[]? Ви просто повинні вказати , який з них ви хочете , наприклад: string.Join("", str.Split((string[])null, StringSplitOptions.RemoveEmptyEntries));. Це насправді те, що робить ваш дзвінок defaultу цьому випадку, оскільки він також повертається null: це допомагає компілятору вирішити, яку перевантаження вибрати. Звідси мій коментар, оскільки твердження у вашому коментарі "Спліт потребує дійсного масиву, а null не зробить ..." є хибним. Нічого страшного, просто подумки варто згадати, оскільки Джейк Дрю запитав, як це працює. +1 для вашої відповіді
Frank J

6
Класна ідея ... але я зробив би це так:string.Concat("H \ne llo Wor ld".Split())
michaelkrisper

3
Рішення michaelkrisper дуже легко читається. Я зробив тест і 'split / join' (162 мілісекунди) виконано краще, ніж 'split / concat' (180 мілісекунд) за 10 000 ітерацій того ж рядка.
kernowcode

45

Спираючись на відповідь Хенкса я створив кілька методів тестування з його відповіддю та деякі додані, більш оптимізовані, методи. Я виявив, що результати відрізняються залежно від розміру вхідного рядка. Тому я перевірив два результативні набори. У найшвидшому способі пов'язане джерело має ще швидший шлях. Але, оскільки він характеризується як небезпечний, я покинув це.

Результати довгого рядка:

  1. InPlaceCharArray: 2021 мс (відповідь Sunsetquest ) - ( Першоджерело )
  2. Рядок розбивається, а потім приєднуйтесь: 4277 мс ( відповідь Керновкода )
  3. Зчитувач струн: 6082 мс
  4. LINQ з використанням рідного char.IsWhitespace: 7357 ms
  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


1
Чому це було знято? Це цілком прийнятно, відповідає вимогам, працює швидше, ніж параметр RegEx і дуже читабельний?
BlueChippy

4
тому що його можна записати набагато коротше: новий рядок (input.Where (c =>! Char.IsWhiteSpace (c)). ToArray ());
Bas Smit

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

Також є декілька приємних незалежних орієнтирів на Stack Overflow від Стіана Стандаля, які також показують, наскільки функція Феліпе приблизно на 300% швидша, ніж наступна швидка функція.


Я спробував перекласти це на C ++, але трохи застряг. Будь-які ідеї, чому мій порт може вийти з ладу? stackoverflow.com/questions/42135922/…
Джон Кейдж

2
Я не можу чинити опір. Подивіться у розділі коментарів статті, на яку ви посилаєтесь. Ви знайдете мене як "програмне забезпечення для кошиків". Він і працював над цим разом деякий час. Я повністю забув про це, коли ця проблема знову з’явилася. Дякую за добрі спогади. :)
Річард Робертсон

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

Мені було б цікаво дізнатись деталі ваших бенчмаркінгу - не те, що я скептично налаштований, але мені цікаво накладні витрати на Linq. Як це було погано?
Марк Меуер

Я не повторно провів усі тести, але я пам’ятаю це дуже багато: все, що стосувалося Linq, було набагато повільніше, ніж усе без нього. Усі розумні використання функцій string та 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());
    }
}

це в основному непотрібна відповідь (регулярний вираз є надмірним, але це швидше рішення, ніж дане - і воно вже прийняте?)
W1ll1amvl

Як можна використовувати методи розширення Linq на рядку? Не можу зрозуміти, за допомогою якого я пропускаю інших, окрімSystem.Linq
GGirard

Ок, схоже, це не доступно в PCL, IErumerable <char> є умовою в реалізації програми String Microsoft ... І я використовую 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();
}

2

Я припускаю, що ваш відповідь XML виглядає приблизно так:

var xml = @"<names>
                <name>
                    foo
                </name>
                <name>
                    bar
                </name>
            </names>";

Найкращий спосіб обробити XML - це використовувати аналізатор XML, наприклад LINQ до XML :

var doc = XDocument.Parse(xml);

var containsFoo = doc.Root
                     .Elements("name")
                     .Any(e => ((string)e).Trim() == "foo");

Після того, як я переконаюсь, що певний тег <name> має належне значення, я закінчую. Не могли б розбору документа мати накладні витрати?
Corey Ogburn

4
Звичайно, у неї є деякі накладні витрати. Але користь має коректність. Рішення, засноване, наприклад, на регулярному вираженні, набагато складніше отримати правильне рішення. Якщо ви визначите, що рішення LINQ до XML занадто повільне, його завжди можна замінити чимось швидше. Але вам слід уникати полювання на найефективнішу реалізацію, перш ніж ви зрозумієте, що правильна надто повільна.
dtb

Це буде працювати на серверах мого роботодавця. Легкий - це те, що я шукаю. Я не хочу того, що "просто працює", але є оптимальним.
Corey Ogburn

4
LINQ to XML - це один із
найлегших

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.
Корі Огберн

Так, перевірити наявність null is
importente

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

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

  • вищевказаний розпорядок виконує це за 25 секунд
  • вищезазначена рутина + заміна окремих символів за 95 секунд
  • регекс абортував через 15 хвилин.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.