Як замінити кілька пробілів одним пробілом у C #?


440

Як я можу замінити декілька пробілів у рядку лише одним пробілом у C #?

Приклад:

1 2 3  4    5

було б:

1 2 3 4 5

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

Я додав орієнтир щодо різних способів зробити це у повторюваному запитанні stackoverflow.com/a/37592018/582061 . Regex був не найшвидшим способом зробити це.
Стіян Штандаль

Відповіді:


468
string sentence = "This is a sentence with multiple    spaces";
RegexOptions options = RegexOptions.None;
Regex regex = new Regex("[ ]{2,}", options);     
sentence = regex.Replace(sentence, " ");

2
У мене є копія та вставка, і це працює. Мені дуже не подобається REgex, але цього разу це рятує моє життя.
Pokus

9
@Craig коментаря буде достатньо, IMO. // Цей блок замінює декілька пробілів одним ... :)
paulwhit

6
Дійсно, RegEx для цього є надмірним.
Joel Coehoorn

11
@Joel: Не можу погодитися. Я фактично впевнений, що цей спосіб є більш ефективним, ніж ваш, для досить великих рядків і його можна виконати в одному рядку. Де надмірність?
Конрад Рудольф

24
@Oscar Joel - код не простий цикл для всіх символів! Це прихований вкладений цикл, який має найгірший квадратичний випадок. Цей регулярний вираз, на відміну від цього, лінійний, створює лише один рядок (= різко зменшив витрати на розподіл порівняно з кодом Джоела), і, крім того, двигун може оптимізувати пекло з нього (якщо чесно, я сумніваюся, що .NET regex є досить розумний для цього, але теоретично цей регулярний вираз може бути реалізований настільки дешево, що навіть не смішно, йому потрібен лише DFA з трьома станами, одним переходом кожен і додаткової інформації немає).
Конрад Рудольф

624

Мені подобається використовувати:

myString = Regex.Replace(myString, @"\s+", " ");

Оскільки він буде вловлювати пробіли будь-якого пробілу (наприклад, вкладки, нові рядки тощо) та замінить їх єдиним пробілом.


43
Незначна модифікація: Regex.Replace (джерело, @ "(\ s) \ s +", "$ 1"); Це поверне перший знайдений пробіл. Тож якщо у вас є 5 вкладок, вона поверне вкладку. Слід вважати, що хтось віддає перевагу цьому.
ФБ десять Кейт

@radistao Ваше посилання призначене для заміни рядка Javascript, а не для C #.
Шива

1
@Shiva, / \ s \ s + / - це стандартний операційний вираз POSIX, який може бути перетворений / використаний на будь-якій мові, використовуючи власний синтаксис
radistao

4
У дусі рішення @ FBtenKate: Regex.Replace (джерело, @ "(\ s) \ 1+", "$ 1"); замінить декілька однакових послідовних символів одним.
Франсуа Бон

1
для того, щоб видалити провідні та відмінні пробіли, слід скористатися функцією Trim () з цією функцією, як var myString = Regex.Replace (myString, @ "\ s +", "") .Trim ();
Harish Nayak

50
string xyz = "1   2   3   4   5";
xyz = string.Join( " ", xyz.Split( new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries ));

6
Це легше читати через регулярний вираз, я вважаю за краще це, тому що мені не потрібно вивчати інший синтаксис
Michael Bahig

9
Мені це подобається, тому що йому не потрібно Regex
AleX_

3
Це було б неефективно для великих струн.
DarcyThomas

3
Це також видаляє провідні та кінцеві місця.
Маці

1
Я також віддаю перевагу цій відповіді. Мій старий наставник говорив: "У будь-який час у вас виникає проблема, яку ви думаєте, що вам потрібно вирішити Regex, ну ... тепер у вас є ДВА проблеми" <wink>
Вільям Мадонна молодший

38

Я думаю, що відповідь Метта найкраща, але я не вірю, що це цілком правильно. Якщо ви хочете замінити нові рядки, ви повинні використовувати:

myString = Regex.Replace(myString, @"\s+", " ", RegexOptions.Multiline);

4
RegexOptions.Multiline змінює значення ^ і $, щоб вони відповідали початку та кінці кожного рядка ($ = \ n), а не цілому рядку. Оскільки \ s еквівалентно [\ f \ n \ r \ t \ v], нові рядки слід замінити, навіть якщо параметр Multiline вимкнено.
SushiGuy

1
Відповідь Метта вже охопила це. Я вважаю, що 30 людей просто зав'язали очима цю відповідь :)
123iamking

26

Ще один підхід, який використовує LINQ:

 var list = str.Split(' ').Where(s => !string.IsNullOrWhiteSpace(s));
 str = string.Join(" ", list);

23

Це набагато простіше, ніж усе, що:

while(str.Contains("  ")) str = str.Replace("  ", " ");

23
Це буде набагато менш ефективно, ніж регулярний вираз "{2,}", якщо рядок містить послідовності з 3 або більше пробілів.
Ян Гойварц

2
@JanGoyvaerts: Навіть з 10 пробілами, регулярний тест був повільнішим, коли я зробив швидкий і брудний тест. За його словами, потрібна лише одна гігантська підрядка, повна пробілів, щоб повністю знищити продуктивність циклу while. Для справедливості я використовував RegexOptions.Compiled, а не повільніший Regex.Replace.
Брайан

5
RegexOptions.Compiled додає багато накладних витрат, компілюючи регулярний вираз в IL. Не використовуйте його, якщо ваша програма не буде використовувати регулярний вираз досить часто або на досить великих рядках, що збільшена швидкість узгодження компенсує зменшену швидкість компіляції.
Ян Гойварц

Це приклад надзвичайно неефективного коду. ЛОЛ.
pcbabu

1
@pcbabu Це не так вже й погано, як здається у багатьох випадках. Replace()Метод буде обробляти всі входження двох просторів в цьому рядку, тому ми не зациклення (і повторне виділення цілого рядка) для кожного екземпляра спарених прогалин в рядку. Один новий розподіл буде обробляти всі вони. Ми повторно запускаємо цикл лише тоді, коли були 3 або більше пробілів разом, що, швидше за все, буде рідкісним явищем для багатьох вхідних джерел. Якщо ви зможете показати, що це стає проблемою для ваших даних, тоді перейдіть до запису стану машини, щоб підштовхнути персонажів за символом до нового струнобудівника.
Joel Coehoorn

21

Regex може бути досить повільним навіть при простих завданнях. Це створює метод розширення, який можна використовувати будь-який string.

    public static class StringExtension
    {
        public static String ReduceWhitespace(this String value)
        {
            var newString = new StringBuilder();
            bool previousIsWhitespace = false;
            for (int i = 0; i < value.Length; i++)
            {
                if (Char.IsWhiteSpace(value[i]))
                {
                    if (previousIsWhitespace)
                    {
                        continue;
                    }

                    previousIsWhitespace = true;
                }
                else
                {
                    previousIsWhitespace = false;
                }

                newString.Append(value[i]);
            }

            return newString.ToString();
        }
    }

Він би використовувався як такий:

string testValue = "This contains     too          much  whitespace."
testValue = testValue.ReduceWhitespace();
// testValue = "This contains too much whitespace."


11

Для тих, хто не любить Regex, ось метод, який використовує StringBuilder:

    public static string FilterWhiteSpaces(string input)
    {
        if (input == null)
            return string.Empty;

        StringBuilder stringBuilder = new StringBuilder(input.Length);
        for (int i = 0; i < input.Length; i++)
        {
            char c = input[i];
            if (i == 0 || c != ' ' || (c == ' ' && input[i - 1] != ' '))
                stringBuilder.Append(c);
        }
        return stringBuilder.ToString();
    }

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

Майте на увазі, що він не видаляє провідні чи кінцеві пробіли, а лише кілька випадків таких.


Якщо ви хочете перевірити, чи є символ пробілом, а не просто пробіл, дивіться мою відповідь нижче .
Ріп

8

Ви можете просто зробити це в одному рядковому рішенні!

string s = "welcome to  london";
s.Replace(" ", "()").Replace(")(", "").Replace("()", " ");

Ви можете вибрати інші дужки (або навіть інші символи), якщо вам подобається.


1
Ви повинні переконатися, що у вашій рядку немає "()" або ") (". Або "wel()come to london)("стає "wel come to london". Ви можете спробувати використовувати багато дужок. Тому використовуйте ((((()))))замість цього, ()а не )))))(((((замість нього )(. Він все одно буде працювати. Все ж, якщо рядок містить ((((()))))або )))))(((((, це не вдасться
nmit026

7

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

temp = new Regex(" {2,}").Replace(temp, " "); 

Якщо ви не надто знайомі з регулярними виразами, ось коротке пояснення:

{2,}Робить пошук регулярних виразів для характеру попереднього, і знаходять подстроки між 2 і необмеженою кількістю раз. Замінює всі матчі в рядку температури з пропуском.
.Replace(temp, " ")

Якщо ви хочете скористатися цим декількома разів, ось кращий варіант, оскільки він створює IL-регулярний вираз під час компіляції:

Regex singleSpacify = new Regex(" {2,}", RegexOptions.Compiled);
temp = singleSpacify.Replace(temp, " ");

7

ні Regex, ні Linq ... не видаляє провідні та кінцеві пробіли, а також зменшує всі вбудовані декілька просторових сегментів до одного

string myString = "   0 1 2  3   4               5  ";
myString = string.Join(" ", myString.Split(new char[] { ' ' }, 
StringSplitOptions.RemoveEmptyEntries));

результат: "0 1 2 3 4 5"


1
Слово обережності: Використання спліт, хоча це дуже просто зрозуміти, може мати напрочуд негативний вплив на ефективність. Оскільки може бути створено багато рядків, вам доведеться переглянути споживання пам'яті, якщо ви обробляєте великі рядки цим методом.
Pac0

5

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

Це можна зробити за допомогою Regex.Replace():

string s = Regex.Replace (
    "   1  2    4 5", 
    @"[ ]{2,}", 
    " "
    );

Або з String.Split():

static class StringExtensions
{
    public static string Join(this IList<string> value, string separator)
    {
        return string.Join(separator, value.ToArray());
    }
}

//...

string s = "     1  2    4 5".Split (
    " ".ToCharArray(), 
    StringSplitOptions.RemoveEmptyEntries
    ).Join (" ");

3

Я щойно написав нове, Joinщо мені подобається, тому я подумав, що відповім на нього:

public static string Join<T>(this IEnumerable<T> source, string separator)
{
    return string.Join(separator, source.Select(e => e.ToString()).ToArray());
}

Однією з цікавих речей у цьому є те, що він працює з колекціями, які не є рядками, викликаючи ToString () на елементи. Використання все одно:

//...

string s = "     1  2    4 5".Split (
    " ".ToCharArray(), 
    StringSplitOptions.RemoveEmptyEntries
    ).Join (" ");

2
навіщо створювати метод розширення? чому не просто використовувати string.Join ()?
Ерік Шкоуновер

3
      // Mysample string
            string str ="hi you           are          a demo";

            //Split the words based on white sapce
            var demo= str .Split(' ').Where(s => !string.IsNullOrWhiteSpace(s));

            //Join the values back and add a single space in between
                    str = string.Join(" ", demo);

//output: string str ="hi you are a demo";

2

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

pattern: (?m:^ +| +$|( ){2,})
replacement: $1

Його трохи важко читати, оскільки ми маємо справу з порожнім простором, тому тут він знову з "пробілами" замінений на "_".

pattern: (?m:^_+|_+$|(_){2,})  <-- don't use this, just for illustration.

Конструкція "(? M:" дозволяє включити "багаторядковий" варіант. Я, як правило, люблю включати будь-які варіанти, які я можу, до самого шаблону, щоб він був більш самостійним.


2

Багато відповідей дають правильний результат, але для тих, хто шукає найкращих вистав, я покращив відповідь Ноланара (яка була найкращою відповіддю для виконання) приблизно на 10%.

public static string MergeSpaces(this string str)
{

    if (str == null)
    {
        return null;
    }
    else
    {
        StringBuilder stringBuilder = new StringBuilder(str.Length);

        int i = 0;
        foreach (char c in str)
        {
            if (c != ' ' || i == 0 || str[i - 1] != ' ')
                stringBuilder.Append(c);
            i++;
        }
        return stringBuilder.ToString();
    }

}

1

Я можу видалити пробіли

while word.contains("  ")  //double space
   word = word.Replace("  "," "); //replace double space by single space.
word = word.trim(); //to remove single whitespces from start & end.

так, але ви б замінили лише два пробіли на один. Це не допоможе X кількості пробілів
MGot90

1
Цей цикл "Хоча" подбає про те, щоб видалити подвійні пробіли.
Learner1947

1

Скористайтеся схемою регулярного вираження

    [ ]+    #only space

   var text = Regex.Replace(inputString, @"[ ]+", " ");

1

спробуйте цей метод

private string removeNestedWhitespaces(char[] st)
{
    StringBuilder sb = new StringBuilder();
    int indx = 0, length = st.Length;
    while (indx < length)
    {
        sb.Append(st[indx]);
        indx++;
        while (indx < length && st[indx] == ' ')
            indx++;
        if(sb.Length > 1  && sb[0] != ' ')
            sb.Append(' ');
    }
    return sb.ToString();
}

використовуйте його так:

string test = removeNestedWhitespaces("1 2 3  4    5".toCharArray());

Це видалить пробіли
The_Black_Smurf

Вибачте за помилку, я виправив код, тепер він працює, як очікувалось, перевірений рядок: "1 2 3 4 9" рядок результатів: "1 2 3 4 9"
Ахмед Аляфф

1

Ось невелика модифікація на Nolonar оригінальну відповідь .

Перевіряючи, чи персонаж не просто пробіл, а будь-який пробіл, використовуйте це:

Він замінить будь-який кілька символів пробілу одним пробілом.

public static string FilterWhiteSpaces(string input)
{
    if (input == null)
        return string.Empty;

    var stringBuilder = new StringBuilder(input.Length);
    for (int i = 0; i < input.Length; i++)
    {
        char c = input[i];
        if (i == 0 || !char.IsWhiteSpace(c) || (char.IsWhiteSpace(c) && 
            !char.IsWhiteSpace(strValue[i - 1])))
            stringBuilder.Append(c);
    }
    return stringBuilder.ToString();
}

0

Старий скол:

string oldText = "   1 2  3   4    5     ";
string newText = oldText
                    .Replace("  ", " " + (char)22 )
                    .Replace( (char)22 + " ", "" )
                    .Replace( (char)22 + "", "" );

Assert.That( newText, Is.EqualTo( " 1 2 3 4 5 " ) );

0

Без використання регулярних виразів:

while (myString.IndexOf("  ", StringComparison.CurrentCulture) != -1)
{
    myString = myString.Replace("  ", " ");
}

Добре використовувати для коротких рядків, але погано виконуватиме довгі рядки з великою кількістю пробілів.


0

Мікс StringBuilder і Enumerable.Aggregate () як метод розширення для рядків:

using System;
using System.Linq;
using System.Text;

public static class StringExtension
{
    public static string StripSpaces(this string s)
    {
        return s.Aggregate(new StringBuilder(), (acc, c) =>
        {
            if (c != ' ' || acc.Length > 0 && acc[acc.Length-1] != ' ')
                acc.Append(c);

            return acc;
        }).ToString();
    }

    public static void Main()
    {
        Console.WriteLine("\"" + StringExtension.StripSpaces("1   Hello       World  2   ") + "\"");
    }
}

Вхід:

"1   Hello       World  2   "

Вихід:

"1 Hello World 2 "
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.