Jak usunąć określone znaki z ciągu w C ++?


96

Na przykład mam użytkownika, który wprowadza numer telefonu.

cout << "Enter phone number: ";
INPUT: (555) 555-5555
cin >> phone;

Chcę usunąć znaki „(”, „)” i „-” z ciągu. Przyjrzałem się ciągowi funkcji usuń, znajdź i zamień, ale widzę tylko, że działają one w oparciu o pozycję.

Czy istnieje funkcja ciągu znaków, której mogę użyć do przekazania znaku, na przykład „(”) i czy ma ona usunąć wszystkie wystąpienia w ciągu?

Odpowiedzi:


140
   string str("(555) 555-5555");

   char chars[] = "()-";

   for (unsigned int i = 0; i < strlen(chars); ++i)
   {
      // you need include <algorithm> to use general algorithms like std::remove()
      str.erase (std::remove(str.begin(), str.end(), chars[i]), str.end());
   }

   // output: 555 5555555
   cout << str << endl;

Aby użyć jako funkcji :

void removeCharsFromString( string &str, char* charsToRemove ) {
   for ( unsigned int i = 0; i < strlen(charsToRemove); ++i ) {
      str.erase( remove(str.begin(), str.end(), charsToRemove[i]), str.end() );
   }
}
//example of usage:
removeCharsFromString( str, "()-" );

4
Jak to działa? Czy użycie wymazywania i usuwania nie jest podwójnym minusem? Dla mnie to brzmi: „usuń znaki, które są na pozycjach, w których () - nie ma”. A skoro każdy jest gotowy na raz, czy nie powinien usunąć WSZYSTKICH znaków? Przeczytałem dokumentację obu funkcji i nie ma to dla mnie sensu. cplusplus.com/reference/algorithm/remove cplusplus.com/reference/string/string/erase
Brent

@Brent std :: remove () NIE usunie żadnych prawidłowych znaków z ciągu, po prostu przenosi prawidłowe znaki razem.
lk_vc

20
@Brent i przyszli czytelnicy, to jest idiom Erase-remove . W skrócie, std::removeprzenosi nieusunięte elementy na przód wektora i zwraca iterator wskazujący tuż poza ostatnim nieusuniętym elementem. Następnie std::eraseprzycina wektor od tego iteratora do końca.
chwarr

1
Dla naprawdę wersji C ++ myślę, że powinniśmy użyć, string chars("()-");a następnie użyć .length()metody, aby uzyskać długość i .at(i)metodę dostępu do znaków :) Funkcjonalne skrzypce - ideone.com/tAZt5I
jave.web

2
Aby użyć jako funkcji: ideone.com/XOROjq - używa<iostream> <algorithm> <cstring>
jave.web

36

Chcę usunąć znaki „(”, „)” i „-” z ciągu.

Możesz użyć std::remove_if()algorytmu, aby usunąć tylko określone znaki:

#include <iostream>
#include <algorithm>
#include <string>

bool IsParenthesesOrDash(char c)
{
    switch(c)
    {
    case '(':
    case ')':
    case '-':
        return true;
    default:
        return false;
    }
}

int main()
{
    std::string str("(555) 555-5555");
    str.erase(std::remove_if(str.begin(), str.end(), &IsParenthesesOrDash), str.end());
    std::cout << str << std::endl; // Expected output: 555 5555555
}

std::remove_if()Algorytm wymaga czegoś nazywanego orzeczenie, które może być wskaźnikiem funkcji jak fragmencie powyżej.

Możesz również przekazać obiekt funkcji (obiekt, który przeciąża ()operator wywołania funkcji ). To pozwala nam stworzyć jeszcze bardziej ogólne rozwiązanie:

#include <iostream>
#include <algorithm>
#include <string>

class IsChars
{
public:
    IsChars(const char* charsToRemove) : chars(charsToRemove) {};

    bool operator()(char c)
    {
        for(const char* testChar = chars; *testChar != 0; ++testChar)
        {
            if(*testChar == c) { return true; }
        }
        return false;
    }

private:
    const char* chars;
};

int main()
{
    std::string str("(555) 555-5555");
    str.erase(std::remove_if(str.begin(), str.end(), IsChars("()- ")), str.end());
    std::cout << str << std::endl; // Expected output: 5555555555
}

Możesz określić, które znaki mają zostać usunięte za pomocą "()- "ciągu. W powyższym przykładzie dodałem spację, aby usunąć spacje, a także nawiasy i myślniki.


Możesz także użyćispunct(int c)
MSalters

Doskonała realizacja. Ta metoda działała bez zarzutu i ma dużo miejsca na dalszą dynamikę. Dziękuję za odpowiedź. MSalters, sprawdzę również funkcję ispunct (int c) i zdam raport z moich działań.
SD.

12

remove_if () została już wspomniana. Ale w C ++ 0x możesz zamiast tego określić predykat za pomocą lambdy.

Poniżej znajduje się przykład tego z 3 różnymi sposobami filtrowania. „Kopiuj” wersje funkcji są również dołączone w przypadkach, gdy pracujesz ze stałą lub nie chcesz modyfikować oryginału.

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

string& remove_chars(string& s, const string& chars) {
    s.erase(remove_if(s.begin(), s.end(), [&chars](const char& c) {
        return chars.find(c) != string::npos;
    }), s.end());
    return s;
}
string remove_chars_copy(string s, const string& chars) {
    return remove_chars(s, chars);
}

string& remove_nondigit(string& s) {
    s.erase(remove_if(s.begin(), s.end(), [](const char& c) {
        return !isdigit(c);
    }), s.end());
    return s;
}
string remove_nondigit_copy(string s) {
    return remove_nondigit(s);
}

string& remove_chars_if_not(string& s, const string& allowed) {
    s.erase(remove_if(s.begin(), s.end(), [&allowed](const char& c) {
        return allowed.find(c) == string::npos;
    }), s.end());
    return s;
}
string remove_chars_if_not_copy(string s, const string& allowed) {
    return remove_chars_if_not(s, allowed);
}

int main() {
    const string test1("(555) 555-5555");
    string test2(test1);
    string test3(test1);
    string test4(test1);
    cout << remove_chars_copy(test1, "()- ") << endl;
    cout << remove_chars(test2, "()- ") << endl;
    cout << remove_nondigit_copy(test1) << endl;
    cout << remove_nondigit(test3) << endl;
    cout << remove_chars_if_not_copy(test1, "0123456789") << endl;
    cout << remove_chars_if_not(test4, "0123456789") << endl;
}

Zamiast const char & c powinienem był naprawdę użyć const string :: value_type &. Ale w tym przypadku to nic wielkiego.
Shadow2531

1
To bardzo dokładna realizacja. Doceniam to i również skorzystam z tej implementacji.
SD.

8

Oto inne rozwiązanie dla wszystkich zainteresowanych. Używa nowego zakresu For w języku c ++ 11

string str("(555) 555-5555");
string str2="";

for (const auto c: str){

    if(!ispunct(c)){

        str2.push_back(c);
    }
}

str = str2;
//output: 555 5555555
cout<<str<<endl;

1
(1) str2inicjalizacja nie jest wymagana. (2) str = std::move(str2)byłoby bardziej wydajne.
Ajay

6

Obawiam się, że nie ma takiego członka dla std :: string, ale można łatwo zaprogramować tego rodzaju funkcje. Może nie jest to najszybsze rozwiązanie, ale wystarczyłoby:

std::string RemoveChars(const std::string& source, const std::string& chars) {
   std::string result="";
   for (unsigned int i=0; i<source.length(); i++) {
      bool foundany=false;
      for (unsigned int j=0; j<chars.length() && !foundany; j++) {
         foundany=(source[i]==chars[j]);
      }
      if (!foundany) {
         result+=source[i];
      }
   }
   return result;
}

EDYCJA: Czytając odpowiedź poniżej, zrozumiałem, że jest bardziej ogólna, nie tylko w celu wykrycia cyfry. Powyższe rozwiązanie pominie każdy znak przekazany w ciągu drugiego argumentu. Na przykład:

std::string result=RemoveChars("(999)99-8765-43.87", "()-");

Spowoduje

99999876543.87

3
using namespace std;


// c++03
string s = "(555) 555-5555";
s.erase(remove_if(s.begin(), s.end(), not1(ptr_fun(::isdigit))), s.end());

// c++11
s.erase(remove_if(s.begin(), s.end(), ptr_fun(::ispunct)), s.end());

Uwaga: możliwe, że będziesz musiał pisać, ptr_fun<int, int>a nie prosteptr_fun


dlaczego to nie jest wybrana odpowiedź?
user3240688

@ user3240688 Zauważ, że std :: ptr_fun jest przestarzałe w C ++ 11 i zostanie usunięte w C ++ 17, a std :: not1 jest przestarzałe w C ++ 17. Możesz użyć std::creflub std::function(lub lambd).
Roi Danton

3

Tak, możesz użyć funkcji isdigit (), aby sprawdzić cyfry :)

Proszę bardzo:

#include <iostream>
#include <cctype>
#include <string.h>

using namespace std;

int main(){

  char *str = "(555) 555-5555";
  int len = strlen(str);

  for (int i=0; i<len; i++){
      if (isdigit(*(str+i))){
        cout << *(str+i);
      }
  }

  cout << endl;


return 0;   
}

Mam nadzieję, że to pomoże :)


Można to zmodyfikować, aby usunąć element, który zwraca wartość false. Dziękuję Ci.
SD.

3

boost::is_any_of

Usuń wszystkie znaki z jednego ciągu, które pojawiają się w innym podanym ciągu:

#include <cassert>

#include <boost/range/algorithm/remove_if.hpp>
#include <boost/algorithm/string/classification.hpp>

int main() {
    std::string str = "a_bc0_d";
    str.erase(boost::remove_if(str, boost::is_any_of("_0")), str.end());
    assert((str == "abcd"));
}

Testowane w Ubuntu 16.04, Boost 1.58.


2

Jeśli masz dostęp do kompilatora obsługującego szablony wariadyczne, możesz użyć tego:

#include <iostream>
#include <string>
#include <algorithm>

template<char ... CharacterList>
inline bool check_characters(char c) {
    char match_characters[sizeof...(CharacterList)] = { CharacterList... };
    for(int i = 0; i < sizeof...(CharacterList); ++i) {
        if(c == match_characters[i]) {
            return true;
        }
    }
    return false;
}

template<char ... CharacterList>
inline void strip_characters(std::string & str) {
    str.erase(std::remove_if(str.begin(), str.end(), &check_characters<CharacterList...>), str.end());
}

int main()
{
    std::string str("(555) 555-5555");
    strip_characters< '(',')','-' >(str);
    std::cout << str << std::endl;
}

1

Oto kolejna alternatywa:

template<typename T>
void Remove( std::basic_string<T> & Str, const T * CharsToRemove )
{
    std::basic_string<T>::size_type pos = 0;
    while (( pos = Str.find_first_of( CharsToRemove, pos )) != std::basic_string<T>::npos )
    {
        Str.erase( pos, 1 ); 
    }
}

std::string a ("(555) 555-5555");
Remove( a, "()-");

Działa z std :: string i std :: wstring


1

Jestem nowy, ale niektóre z powyższych odpowiedzi są niesamowicie skomplikowane, więc oto alternatywa.

UWAGA: Dopóki 0-9 są ciągłe (co powinno być zgodne ze standardem), powinno to odfiltrować wszystkie inne znaki oprócz liczb i ''. Wiedząc, że cyfry 0-9 powinny być ciągłe, a znak jest w rzeczywistości liczbą int, możemy wykonać poniższe czynności.

EDYCJA: Nie zauważyłem, że plakat też chciał spacje, więc zmieniłem to ...

#include <cstdio>
#include <cstring>

void numfilter(char * buff, const char * string)
{
  do
  { // According to standard, 0-9 should be contiguous in system int value.
    if ( (*string >= '0' && *string <= '9') || *string == ' ')
      *buff++ = *string;
  } while ( *++string );
  *buff++ = '\0'; // Null terminate
}

int main()
{
  const char *string = "(555) 555-5555";
  char buff[ strlen(string) + 1 ];

  numfilter(buff, string);
  printf("%s\n", buff);

return 0;
}

Poniżej znajduje się filtrowanie dostarczonych znaków.

#include <cstdio>
#include <cstring>

void cfilter(char * buff, const char * string, const char * toks)
{
  const char * tmp;  // So we can keep toks pointer addr.
  do
  {
    tmp = toks;
    *buff++ = *string; // Assume it's correct and place it.
    do                 // I can't think of a faster way.
    {
      if (*string == *tmp)
      {
        buff--;  // Not correct, pull back and move on.
        break;
      }
    }while (*++tmp);
  }while (*++string);

  *buff++ = '\0';  // Null terminate
}

int main()
{
  char * string = "(555) 555-5555";
  char * toks = "()-";
  char buff[ strlen(string) + 1 ];

  cfilter(buff, string, toks);
  printf("%s\n", buff);

  return 0;
}

To nie spełnia oczekiwań PO; usuwa również spacje.
Andrew Barber,

1

Przy użyciu std :: wstring i wchar_t (wymaga nagłówka Unicode ):

//#include <tchar.h>
std::wstring phone(L"(555) 555-5555");

... dalej fantazyjny inicjator zakresu statycznego; nie trzeba konfigurować badChars2 dokładnie w ten sam sposób. To przesada; bardziej akademicki niż cokolwiek innego:

const wchar_t *tmp = L"()-"; 
const std::set<wchar_t> badChars2(tmp,tmp + sizeof(tmp)-1);

Prosta, zwięzła lambda:

  1. Używa telefonu na liście przechwytywania lambda.
  2. Używa idiomu Erase-remove
  3. Usuwa wszystkie złe postacie z telefonu

    for_each(badChars2.begin(), badChars2.end(), [&phone](wchar_t n){
         phone.erase(std::remove(phone.begin(), phone.end(), n), phone.end());
    });
    wcout << phone << endl;

Dane wyjściowe: „555 5555555”


1

Dla tych z Was, którzy wolą bardziej zwięzły, łatwiejszy do odczytania styl kodowania lambda ...

Ten przykład usuwa wszystkie znaki inne niż alfanumeryczne i białe znaki z szerokiego ciągu. Możesz go zmieszać z innymi funkcjami pomocniczymi ctype.h, aby usunąć złożone testy znakowe.

(Nie jestem pewien, jak te funkcje poradziłyby sobie z językami CJK, więc idź tam cicho).

    // Boring C loops: 'for(int i=0;i<str.size();i++)' 
    // Boring C++ eqivalent: 'for(iterator iter=c.begin; iter != c.end; ++iter)'

Sprawdź, czy nie jest to łatwiejsze do zrozumienia niż hałaśliwe pętle C / C ++ for / iterator:

TSTRING label = _T("1.   Replen & Move  RPMV");
TSTRING newLabel = label;
set<TCHAR> badChars; // Use ispunct, isalpha, isdigit, et.al. (lambda version, with capture list parameter(s) example; handiest thing since sliced bread)
for_each(label.begin(), label.end(), [&badChars](TCHAR n){
    if (!isalpha(n) && !isdigit(n))
        badChars.insert(n);
});

for_each(badChars.begin(), badChars.end(), [&newLabel](TCHAR n){
    newLabel.erase(std::remove(newLabel.begin(), newLabel.end(), n), newLabel.end());
});

newLabel wyniki po uruchomieniu tego kodu: „ 1ReplenMoveRPMV

To jest tylko akademickie, ponieważ byłoby wyraźnie bardziej precyzyjne, zwięzłe i wydajniejsze połączenie logiki „if” z lambda0 (pierwsza for_each ) w pojedynczą lambda1 (druga for_each ), jeśli już ustaliłeś, które znaki są „badChars” .


Podziękowania dla odpowiedzi @Eric Z za wspomnienie i użycie poręcznego idiomu Erase-remove. en.wikipedia.org/wiki/Erase-remove_idiom
Darrin

0

Wiele dobrych odpowiedzi, oto inny sposób na uporządkowanie ciągu liczb, nie polega na usuwaniu znaków, ale na usunięciu liczb.

string str("(555) 555-5555"), clean;
for (char c : str)
    if (c >= 48 and c <= 57)
        clean.push_back(c);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.