Jak zamienić wiele spacji na jedną spację w C #?

440

Jak mogę zastąpić wiele spacji w ciągu tylko jedną spacją w C #?

Przykład:

1 2 3  4    5

byłoby:

1 2 3 4 5
Pokus
źródło
1
automat państwowy może to łatwo zrobić, ale prawdopodobnie jest to przesada, jeśli potrzebujesz go tylko do usuwania spacji
Adrian
Dodałem test porównawczy na różne sposoby, aby to zrobić w duplikacie pytania stackoverflow.com/a/37592018/582061 . Regex nie był najszybszym sposobem na to.
Stian Standahl

Odpowiedzi:

468
string sentence = "This is a sentence with multiple    spaces";
RegexOptions options = RegexOptions.None;
Regex regex = new Regex("[ ]{2,}", options);     
sentence = regex.Replace(sentence, " ");
Patrick Desjardins
źródło
2
Mam to skopiować i wkleić i to działa. Naprawdę nie lubię REgex, ale tym razem ratuje mi życie.
Pokus
9
@Craig wystarczy komentarz, IMO. // Ten blok zastępuje wiele spacji jednym ... :)
paulwhit
6
Naprawdę, RegEx jest do tego przesadny.
Joel Coehoorn
11
@Joel: Nie mogę się zgodzić. Jestem pewien, że ten sposób jest bardziej wydajny niż twój dla wystarczająco dużych ciągów i można to zrobić w jednym wierszu. Gdzie jest przesada?
Konrad Rudolph,
24
@Oscar Kod Joela nie jest prostą pętlą wszystkich znaków! Jest to ukryta zagnieżdżona pętla, która ma kwadratowy najgorszy przypadek. Z kolei to wyrażenie regularne jest liniowe, tworzy tylko jeden ciąg (= drastycznie zmniejszone koszty alokacji w porównaniu z kodem Joela), a ponadto silnik może go zoptymalizować (szczerze mówiąc, wątpię, czy wyrażenie regularne .NET to wystarczająco inteligentne, ale teoretycznie to wyrażenie regularne można wdrożyć tak tanio, że nie jest już nawet śmieszne; potrzebuje tylko DFA z trzema stanami, jednym przejściem i bez dodatkowych informacji).
Konrad Rudolph
624

Lubię używać:

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

Ponieważ wyłapuje biegi dowolnego rodzaju białych znaków (np. Tabulatory, znaki nowej linii itp.) I zastępuje je pojedynczą spacją.

Matt
źródło
43
Nieznaczna modyfikacja: Regex.Replace (source, @ "(\ s) \ s +", "$ 1"); Zwróci pierwszy znaleziony typ białych znaków. Więc jeśli masz 5 kart, zwróci kartę. Jeśli ktoś woli to.
FB ten Kate
@radistao Twój link zastępuje ciąg JavaScript, a nie C #.
Shiva
1
@Shiva, / \ s \ s + / to standardowa instrukcja wyrażenia regularnego POSIX i może być konwertowana / używana w dowolnym języku przy użyciu własnej składni
radistao
4
W duchu rozwiązania @ FBtenKate: Regex.Replace (source, @ "(\ s) \ 1+", "$ 1"); zastąpi wiele identycznych kolejnych znaków jednym.
François Beaune,
1
w celu usunięcia początkowych i końcowych białych znaków należy użyć funkcji Trim () z tym ,, jak 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 ));
tvanfosson
źródło
6
Jest to bardziej czytelne niż regex, wolę to bardziej, ponieważ nie muszę się uczyć innej składni
Michael Bahig
9
Podoba mi się, ponieważ nie potrzebuje Regex
AleX_
3
Byłoby to nieefektywne w przypadku dużych ciągów.
DarcyThomas,
3
Usuwa to również spacje wiodące i końcowe.
Matzi
1
Ja też wolę tę odpowiedź. Mój stary mentor mawiał: „za każdym razem, gdy masz problem, który Twoim zdaniem potrzebujesz Regex do rozwiązania, cóż ... teraz masz DWIE problemy” <wink>
William Madonna Jr.
38

Myślę, że odpowiedź Matta jest najlepsza, ale nie sądzę, żeby była słuszna. Jeśli chcesz zastąpić znaki nowej linii, musisz użyć:

myString = Regex.Replace(myString, @"\s+", " ", RegexOptions.Multiline);
Brenda Bell
źródło
4
RegexOptions.Multiline zmienia znaczenie ^ i $, tak aby pasowały do ​​początku i końca każdej linii ($ = \ n), zamiast całego ciągu wielowierszowego. Ponieważ \ s jest równoważne z [\ f \ n \ r \ t \ v], nowe linie powinny zostać zastąpione, nawet jeśli opcja Multiline jest wyłączona.
SushiGuy
1
Odpowiedź Matta już to omówiła. „Wierzę”, że 30 osób właśnie z zawiązanymi oczami podniosło głos na tę odpowiedź :)
123iamking,
26

Inne podejście wykorzystujące LINQ:

 var list = str.Split(' ').Where(s => !string.IsNullOrWhiteSpace(s));
 str = string.Join(" ", list);
cuongle
źródło
23

To o wiele prostsze niż wszystko:

while(str.Contains("  ")) str = str.Replace("  ", " ");
Joel Coehoorn
źródło
23
Będzie to znacznie mniej wydajne niż wyrażenie regularne {2,}, jeśli ciąg zawiera sekwencje 3 lub więcej spacji.
Jan Goyvaerts,
2
@JGGoyvaerts: Nawet z 10 spacjami regex był wolniejszy, kiedy wykonałem szybki i brudny test. To powiedziawszy, wystarczy jeden gigantyczny substrat pełen przestrzeni, aby całkowicie zabić działanie pętli while. Dla uczciwości użyłem RegexOptions.Compiled zamiast wolniejszego Regex.Replace.
Brian
5
RegexOptions.Compiled dodaje wiele narzutów związanych z kompilacją wyrażenia regularnego w IL. Nie używaj go, chyba że twoja aplikacja będzie używała wyrażenia regularnego wystarczająco często lub na wystarczająco dużych ciągach, aby zwiększona dopasowana prędkość kompensowała zmniejszoną prędkość kompilacji.
Jan Goyvaerts
To jest przykład skrajnie nieefektywnego kodu. LOL.
pcbabu
1
@pcbabu Nie jest tak źle, jak się wydaje w wielu przypadkach. Replace()Metoda będzie obsługiwać wszystkie wystąpienia dwóch miejsc w danym ciągu znaków, więc nie jesteśmy w pętli (i realokacji cały łańcuch) dla każdej instancji sparowanych przestrzeni w ciąg. Jeden nowy przydział obsłuży je wszystkie. Ponownie uruchamiamy pętlę tylko wtedy, gdy są razem 3 lub więcej spacji, co może być rzadsze w przypadku wielu źródeł wejściowych. Jeśli możesz pokazać, że staje się to problemem dla twoich danych, to idź napisz maszynę stanu, aby wypychać znak po znaku do nowego programu budującego łańcuchy.
Joel Coehoorn
21

Regex może być powolny nawet przy prostych zadaniach. Tworzy to metodę rozszerzenia, której można użyć z dowolnej 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();
        }
    }

Byłby używany jako taki:

string testValue = "This contains     too          much  whitespace."
testValue = testValue.ReduceWhitespace();
// testValue = "This contains too much whitespace."
ScubaSteve
źródło
15
myString = Regex.Replace(myString, " {2,}", " ");
Jan Goyvaerts
źródło
11

Dla tych, którzy nie lubią Regex, oto metoda, która wykorzystuje 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();
    }

W moich testach ta metoda była średnio 16 razy szybsza przy bardzo dużym zestawie łańcuchów od małych do średnich, w porównaniu do regexu skompilowanego statycznie. W porównaniu z nieskompilowanym lub niestatycznym regeksem powinno to być jeszcze szybsze.

Pamiętaj, że nie usuwa początkowych ani końcowych spacji, a tylko ich wielokrotne wystąpienie.

Nolonar
źródło
Jeśli chcesz sprawdzić, czy znak jest spacją, a nie tylko spacją, zobacz moją odpowiedź poniżej .
Reap
8

Możesz to zrobić w jednym rozwiązaniu!

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

Możesz wybrać inne nawiasy (lub nawet inne znaki), jeśli chcesz.

ravish.hacker
źródło
1
Musisz upewnić się, że Twój ciąg nie zawiera „()” lub „) (”. Lub stanie "wel()come to london)("się "wel come to london". Możesz spróbować użyć wielu nawiasów. Więc użyj ((((()))))zamiast ()i )))))(((((zamiast )(. Będzie nadal działać. Wciąż, jeśli ciąg zawiera ((((()))))lub )))))(((((, to się nie powiedzie
nmit026
7

Jest to krótsza wersja, z której należy korzystać tylko wtedy, gdy robisz to tylko raz, ponieważ tworzy ona nową instancję Regexklasy za każdym razem, gdy jest wywoływana.

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

Jeśli nie znasz zbyt dobrze wyrażeń regularnych, oto krótkie wyjaśnienie:

{2,}Sprawia, że przeszukiwanie regex dla znaku poprzedzającego go i wyszukuje podciągi między 2 i nieograniczony czas. Zastępuje wszystkie mecze w temp ciąg z kosmosu.
.Replace(temp, " ")

Jeśli chcesz użyć tego wielokrotnie, oto lepsza opcja, ponieważ tworzy ona wyrażenie regularne w czasie kompilacji:

Regex singleSpacify = new Regex(" {2,}", RegexOptions.Compiled);
temp = singleSpacify.Replace(temp, " ");
ktoś
źródło
7

bez Regex, bez Linq ... usuwa początkowe i końcowe spacje, a także redukuje osadzone segmenty wielu spacji do jednego spacji

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

wynik: „0 1 2 3 4 5”

Stephen du Buis
źródło
1
Słowo ostrzeżenia: użycie podziału, choć bardzo proste do zrozumienia, może mieć zaskakująco negatywny wpływ na wydajność. Ponieważ można utworzyć wiele ciągów, będziesz musiał obserwować zużycie pamięci w przypadku obsługi dużych ciągów za pomocą tej metody.
Pac0,
5

Pocieszając inne odpowiedzi, według Joela, i mam nadzieję, że nieco się poprawię:

Możesz to zrobić za pomocą Regex.Replace():

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

Lub z 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 (" ");
Jay Bazuzi
źródło
3

Właśnie napisałem nowy, Joinktóry mi się podoba, więc pomyślałem, że odpowiem ponownie:

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

Jedną z fajnych rzeczy jest to, że działa z kolekcjami, które nie są łańcuchami, wywołując ToString () na elementach. Użycie jest nadal takie samo:

//...

string s = "     1  2    4 5".Split (
    " ".ToCharArray(), 
    StringSplitOptions.RemoveEmptyEntries
    ).Join (" ");
Jay Bazuzi
źródło
2
po co tworzyć metodę rozszerzenia? dlaczego nie użyć po prostu string.Join ()?
Eric Schoonover,
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";
JIYAUL MUSTAPHA
źródło
2

Wiem, że to dość stare, ale natknąłem się na to, próbując osiągnąć prawie to samo. Znaleziono to rozwiązanie w RegEx Buddy. Ten wzór zastąpi wszystkie podwójne spacje pojedynczymi spacjami, a także przycina początkowe i końcowe spacje.

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

Jest trochę trudny do odczytania, ponieważ mamy do czynienia z pustą przestrzenią, więc tutaj znowu jest to z „spacjami” zastąpionymi przez „_”.

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

Konstrukcja „(? M:” włącza opcję „wielu linii”. Zazwyczaj lubię zawrzeć dowolne opcje w obrębie samego wzorca, aby był bardziej samodzielny.

Paul Easter
źródło
2

Wiele odpowiedzi zapewnia właściwą wydajność, ale dla tych, którzy szukają najlepszych wyników, poprawiłem odpowiedź Nolanara (która była najlepszą odpowiedzią na wydajność) o około 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();
    }

}
The_Black_Smurf
źródło
1

Dzięki temu mogę usunąć białe spacje

while word.contains("  ")  //double space
   word = word.Replace("  "," "); //replace double space by single space.
word = word.trim(); //to remove single whitespces from start & end.
Uczeń 1947
źródło
tak, ale zastąpiłbyś tylko dwie białe spacje jedną. Nie pomogłoby to X spacji
MGot90
1
Pętla While zadba o usunięcie wszystkich podwójnych spacji.
Learner1947
1

Użyj wzorca wyrażenia regularnego

    [ ]+    #only space

   var text = Regex.Replace(inputString, @"[ ]+", " ");
M.Hassan
źródło
1

wypróbuj tę metodę

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

użyj tego w ten sposób:

string test = removeNestedWhitespaces("1 2 3  4    5".toCharArray());
Ahmed Aljaff
źródło
Spowoduje to usunięcie spacji końcowych
The_Black_Smurf
przepraszam za błąd, naprawiłem kod, teraz działa zgodnie z oczekiwaniami ciąg
testowy
1

Oto niewielkie modyfikacje na Nolonar oryginalnej odpowiedzi .

Aby sprawdzić, czy znak nie jest tylko spacją, ale dowolną spacją, użyj tego:

Zastąpi wiele znaków białych znaków pojedynczą spacją.

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();
}
Zbierać
źródło
0

Stary skool:

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 " ) );
oneedaywhen
źródło
0

Bez użycia wyrażeń regularnych:

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

OK, aby używać na krótkich ciągach, ale będzie źle działać na długich ciągach z dużą ilością spacji.

Tom Gullen
źródło
0

Mieszanka StringBuilder i Enumerable.Aggregate () jako metody rozszerzenia ciągów:

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

Wejście:

"1   Hello       World  2   "

Wynik:

"1 Hello World 2 "
Patrick Artner
źródło