Usuń spacje ze std :: string w C ++

222

Jaki jest preferowany sposób usuwania spacji z łańcucha w C ++? Mógłbym zapętlić wszystkie postacie i zbudować nowy ciąg, ale czy jest lepszy sposób?

Steve Hanov
źródło

Odpowiedzi:

257

Najlepiej jest użyć algorytmu remove_ifi isspace:

remove_if(str.begin(), str.end(), isspace);

Teraz sam algorytm nie może zmienić kontenera (tylko modyfikować wartości), więc faktycznie przetasowuje wartości i zwraca wskaźnik do miejsca, w którym powinien być teraz koniec. Musimy więc wywołać string :: erase, aby faktycznie zmodyfikować długość kontenera:

str.erase(remove_if(str.begin(), str.end(), isspace), str.end());

Należy również pamiętać, że remove_if utworzy co najwyżej jedną kopię danych. Oto przykładowa implementacja:

template<typename T, typename P>
T remove_if(T beg, T end, P pred)
{
    T dest = beg;
    for (T itr = beg;itr != end; ++itr)
        if (!pred(*itr))
            *(dest++) = *itr;
    return dest;
}
Cena matowa
źródło
54
Ponieważ „isspace” ma przeciążenia, prawdopodobnie będziesz musiał zakwalifikować ogólny kod do użycia :: isspace (implementacja C, która nie przyjmuje ustawień regionalnych) lub zostać przywitanym za pomocą tajemniczych błędów tworzenia szablonów.
Bklyn
4
Wszystko - bądź ostrożny z powyższą metodą (dwie pojedyncze linie, nie wersja z szablonem, chociaż może mieć ten sam problem). Użyłem go w projekcie, nie zdając sobie sprawy, że nie zawsze jest to poprawne. Na przykład, jeśli przekażesz ciąg „1 + 1”, zwróci „1 + 11”. Przełączyłem się na metodę @rupello poniżej i to zadziałało dobrze w tym przypadku. Miłego kodowania!
JoeB
6
@Joe W odpowiedzi wyraźnie wspomniano, że należy erasepóźniej zadzwonić . To zwróci poprawny wynik.
Konrad Rudolph
31
-1 to użycie isspacejest UB dla wszystkich zestawów znaków oprócz oryginalnego 7-bitowego ASCII. C99 § 7.4 / 1. to nie dziwi mnie, że to było upvoted w wysokości 71 głosami teraz, mimo że bardzo złe rady.
Pozdrawiam i hth. - Alf
16
Wystarczy powtórzyć, kod w tej odpowiedzi przekazuje wartości ujemne (inne niż EOF) do isspace, dla wszystkich znaków spoza ASCII, z domyślnym wyborem sygnatury w praktyce char. W ten sposób ma niezdefiniowane zachowanie . Powtarzam to, ponieważ podejrzewam celową próbę zatopienia tego faktu w hałasie.
Pozdrawiam i hth. - Alf
100
std::string::iterator end_pos = std::remove(str.begin(), str.end(), ' ');
str.erase(end_pos, str.end());
Arno
źródło
31
Moje głosowanie za kanonicznym wymazaniem / usunięciem idiomu. Może być przekształcony w jedną linijkę: str.erase (std :: remove (str.begin (), str.end (), ''), str.end ());
Bklyn
11
Uwaga: Musisz to uwzględnić, <algorithm>aby to działało.
Tara,
37

Od gamedev

string.erase(std::remove_if(string.begin(), string.end(), std::isspace), string.end());
rupello
źródło
22
Nie będzie się to kompilowało w implementacjach zgodnych ze standardami z powodu przeładowania lokalizacji przez std :: isspace. Musisz użyć :: isspace lub wykonać kilka nieczytelnych machinacji za pomocą std :: bind2nd. Czy ogólny kod nie jest piękny?
Bklyn
Zauważ też, że jeśli którykolwiek ze znaków jest ujemny (np. Znak UTF8 po podpisaniu znaku), użycie ::isspaceto UB.
Martin Bonner obsługuje Monikę
30

Czy możesz użyć Boost String Algo? http://www.boost.org/doc/libs/1_35_0/doc/html/string_algo/usage.html#id1290573

erase_all(str, " "); 
Nemanja Trifunovic
źródło
3
Jest wolniejszy niż wspomniany remove_if(str.begin(), str.end(), isspace);Matt Price. Nie wiem dlaczego. W rzeczywistości wszystkie elementy wspomagające, które mają alternatywy STL, są wolniejsze niż odpowiadające im gcc (wszystkie te, które testowałem). Niektóre z nich są znacznie wolniejsze! (do 5 razy we wstawkach unordered_map) Być może dzieje się tak z powodu pamięci podręcznej procesora w udostępnianym środowisku lub czegoś podobnego.
Etherealone
16

Do przycinania użyj algorytmów ciągu wzmocnionego :

#include <boost/algorithm/string.hpp>

using namespace std;
using namespace boost;

// ...

string str1(" hello world! ");
trim(str1);      // str1 == "hello world!"
rzymski
źródło
15

Możesz użyć tego rozwiązania do usunięcia znaku:

#include <algorithm>
#include <string>
using namespace std;

str.erase(remove(str.begin(), str.end(), char_to_remove), str.end());
użytkownik2281802
źródło
1
#include <string.h> using namespace std;
slackmart
To rozwiązanie jest dla mnie odpowiednie. Pierwsza nie jest.
Jason Liu
1
należy unikać używania przestrzeni nazw std. stackoverflow.com/questions/1452721/…
infinitezero
12

Cześć, możesz zrobić coś takiego. Ta funkcja usuwa wszystkie spacje.

string delSpaces(string &str) 
{
   str.erase(std::remove(str.begin(), str.end(), ' '), str.end());
   return str;
}

Zrobiłem inną funkcję, która usuwa wszystkie niepotrzebne spacje.

string delUnnecessary(string &str)
{
    int size = str.length();
    for(int j = 0; j<=size; j++)
    {
        for(int i = 0; i <=j; i++)
        {
            if(str[i] == ' ' && str[i+1] == ' ')
            {
                str.erase(str.begin() + i);
            }
            else if(str[0]== ' ')
            {
                str.erase(str.begin());
            }
            else if(str[i] == '\0' && str[i-1]== ' ')
            {
                str.erase(str.end() - 1);
            }
        }
    }
    return str;
}
ddacot
źródło
8
string replaceinString(std::string str, std::string tofind, std::string toreplace)
{
        size_t position = 0;
        for ( position = str.find(tofind); position != std::string::npos; position = str.find(tofind,position) )
        {
                str.replace(position ,1, toreplace);
        }
        return(str);
}

Użyj tego:

string replace = replaceinString(thisstring, " ", "%20");
string replace2 = replaceinString(thisstring, " ", "-");
string replace3 = replaceinString(thisstring, " ", "+");
SudoBash
źródło
7

Jeśli chcesz to zrobić za pomocą łatwego makra, oto jedno:

#define REMOVE_SPACES(x) x.erase(std::remove(x.begin(), x.end(), ' '), x.end())

Zakłada się, że zrobiłeś #include <string> oczywiście.

Nazwij to tak:

std::string sName = " Example Name ";
REMOVE_SPACES(sName);
printf("%s",sName.c_str()); // requires #include <stdio.h>
Volomike
źródło
5
dlaczego używałbyś do tego makra?
dani
1
Mniej pisania na klawiaturze dla typowego zadania.
Volomike,
3
Równie skrótem dla strony wywołującej jest wywołanie funkcji przyjmującej odwołanie do wartości do łańcucha. Makra mogą mieć zaskakujące zachowania wchodzące w interakcje z ich argumentami (szczególnie z efektami ubocznymi), ale gorzej, jeśli są zaangażowane w błąd, ich nazwy nie pojawiają się w komunikatach kompilatora, ich implementacja tak.
Chris Uzdavinis,
2

Długo korzystałem z poniższej metody - nie jestem pewien jej złożoności.

s.erase(std::unique(s.begin(),s.end(),[](char s,char f){return (f==' '||s==' ');}),s.end());

kiedy chcesz usunąć postać, ' 'a niektóre na przykład - użyć

s.erase(std::unique(s.begin(),s.end(),[](char s,char f){return ((f==' '||s==' ')||(f=='-'||s=='-'));}),s.end());

podobnie po prostu zwiększ || liczbę, jeśli liczba znaków, które chcesz usunąć, nie wynosi 1

ale jak wspominają inni, idiom usuwania usuń również wydaje się w porządku.

RaGa__M
źródło
1
string removeSpaces(string word) {
    string newWord;
    for (int i = 0; i < word.length(); i++) {
        if (word[i] != ' ') {
            newWord += word[i];
        }
    }

    return newWord;
}

Ten kod w zasadzie pobiera ciąg znaków i iteruje wszystkie zawarte w nim znaki. Następnie sprawdza, czy ten ciąg jest białą spacją, jeśli nie jest, to znak jest dodawany do nowego ciągu.

Chrupiące Jabłka
źródło
1
   #include <algorithm>
   using namespace std;

   int main() {
       .
       .
       s.erase( remove( s.begin(), s.end(), ' ' ), s.end() );
       .
       .
   }

Źródło:

Referencje zaczerpnięte z tego forum.

Jan
źródło
1
To tak naprawdę nie dodaje niczego więcej niż ta odpowiedź już. Czy możesz podać więcej wyjaśnień lub szczegółów, aby Twoja odpowiedź była lepsza i warta kontynuowania tego pytania?
Das_Geek
Myślę, że jest to prostsze , ponieważ robi to samo w jednym stwierdzeniu.
Jan
2
Wspaniały! Następnie umieść to rozumowanie jako wyjaśnienie bezpośrednio w swojej odpowiedzi . Pierwotne pytanie ma ponad jedenaście lat i bez uzasadnienia twoja odpowiedź może być postrzegana jako hałas w porównaniu z innymi zaakceptowanymi, dobrze ocenionymi odpowiedziami. Posiadanie takiego wyjaśnienia pomoże zapobiec usunięciu odpowiedzi.
Das_Geek
To byłby dobry , ale nie mogłem, że jak mam umieścić że na moją odpowiedź ... że moja odpowiedź jest lepsza niż ta odpowiedź . ? Z wielką przyjemnością zredagowałbyś moją odpowiedź.
Jan
2
Niestety edytowanie odpowiedzi w celu dodania tej treści byłoby niezgodne z wytycznymi dotyczącymi edycji , a moja edycja prawdopodobnie zostałaby odrzucona lub wycofana później. Możesz użyć pierwszego linku w tym komentarzu, aby samodzielnie edytować odpowiedź. Całkowicie dopuszczalne jest stwierdzenie, że uważasz, że twoja odpowiedź jest lepsza niż jakakolwiek inna, i uzasadnienie tego. Społeczność zdecyduje, czy masz rację, głosując w górę lub w dół.
Das_Geek
0

W C ++ 20 możesz używać darmowej funkcji std :: erase

std::string str = " Hello World  !";
std::erase(str, ' ');

Pełny przykład:

#include<string>
#include<iostream>

int main() {
    std::string str = " Hello World  !";
    std::erase(str, ' ');
    std::cout << "|" << str <<"|";
}

Drukuję | tak, że jest oczywiste, że przestrzeń na początku jest również usuwana.

Uwaga: usuwa to tylko spację, a nie każdą inną możliwą postać, którą można uznać za spację, patrz https://en.cppreference.com/w/cpp/string/byte/isspace

NoSenseEtAl
źródło
0

Usuwa wszystkie znaki spacji, takie jak tabulatory i podziały wierszy (C ++ 11):

string str = " \n AB cd \t efg\v\n";
str = regex_replace(str,regex("\\s"),"");
AnselmRu
źródło
Dlaczego poleciłbyś takie podejście zamiast zaakceptowanej odpowiedzi @ Matt-Price sprzed ponad dekady?
Jeremy Caney
Niech wszystkie rozwiązania zostaną tutaj przedstawione. Może ktoś będzie potrzebował tego rozwiązania.
AnselmRu
Nie sprzeciwiam się temu. Mówię, aby ułatwić ludziom ocenę różnych podejść, wyjaśniając różnice i jakie scenariusze mogą być bardziej odpowiednie.
Jeremy Caney
1
Prawdopodobnie to rozwiązanie nie jest najbardziej ekonomiczne, ale pozwala pozbyć się wszystkich białych znaków , a nie tylko spacji.
AnselmRu
0
  string str = "2C F4 32 3C B9 DE";
  str.erase(remove(str.begin(),str.end(),' '),str.end());
  cout << str << endl;

wyjście: 2CF4323CB9DE

Kerim FIRAT
źródło
-1
string removespace(string str)
{    
    int m = str.length();
    int i=0;
    while(i<m)
    {
        while(str[i] == 32)
        str.erase(i,1);
        i++;
    }    
}
test c
źródło
3
Ogólnie zaleca się dodanie krótkiego wyjaśnienia do odpowiedzi na kod.
arcyqwerty
1
@test - length()zwraca a size_t, a nie an int. erase()bierze, a size_typenie int. Funkcja prawdopodobnie zawiedzie, jeśli napotkane zostaną dwie kolejne spacje, ponieważ indeks jest zawsze zwiększany. Jeśli jedna spacja zostanie usunięta, pętla będzie czytać poza granicami łańcucha. Prawdopodobnie powinieneś usunąć tę odpowiedź, ponieważ wymaga ona dużej pomocy.
jww
-3

Obawiam się, że to najlepsze rozwiązanie, jakie mogę wymyślić. Możesz jednak użyć funkcji replace (), aby wstępnie przydzielić wcześniej minimalną wymaganą pamięć, aby nieco przyspieszyć. Otrzymasz nowy ciąg znaków, który prawdopodobnie będzie krótszy, ale zajmuje tyle samo pamięci, ale unikniesz realokacji.

EDYCJA: W zależności od twojej sytuacji, może to powodować mniejsze obciążenie ogólne niż pomieszanie postaci.

Powinieneś wypróbować różne podejścia i przekonać się, co jest dla Ciebie najlepsze: możesz nie mieć żadnych problemów z wydajnością.

Dave Van den Eynde
źródło
remove_if tworzy co najwyżej jedną kopię każdej wartości. Tak więc naprawdę nie ma tak dużego narzutu w stosunku do tego, co należy zrobić.
Matt Price