Comment supprimer certains caractères d'une chaîne en C ++?

96

Par exemple, un utilisateur saisit un numéro de téléphone.

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

Je souhaite supprimer les caractères "(", ")" et "-" de la chaîne. J'ai regardé les fonctions de suppression, de recherche et de remplacement de chaîne, mais je vois uniquement qu'elles fonctionnent en fonction de la position.

Existe-t-il une fonction de chaîne que je peux utiliser pour passer un caractère, "(" par exemple, et lui demander de supprimer toutes les instances d'une chaîne?

DAKOTA DU SUD.
la source

Réponses:

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;

Pour utiliser comme fonction :

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, "()-" );
Eric Z
la source
4
Comment cela marche-t-il? N'est-ce pas un double négatif d'utiliser l'effacement et la suppression? Pour moi, cela se lit comme suit: "effacez les caractères qui sont dans des positions où () - ne sont pas." Et puisque chacun est fait à la fois, ne devrait-il pas supprimer TOUS les caractères? J'ai lu la documentation sur les deux fonctions, et cela n'a aucun sens pour moi. cplusplus.com/reference/algorithm/remove cplusplus.com/reference/string/string/erase
Brent
@Brent std :: remove () ne supprimera PAS de caractères valides de la chaîne, il déplace simplement les caractères valides ensemble.
lk_vc
20
@Brent et futurs lecteurs, c'est l' idiome Erase-remove . En bref, std::removedéplace les éléments non supprimés vers l'avant du vecteur et renvoie un itérateur pointant juste au-delà du dernier élément non supprimé. Puis std::erasecoupe le vecteur de cet itérateur à la fin.
chwarr
1
Pour la version vraiment C ++, je pense que nous devrions utiliser string chars("()-");puis utiliser la .length()méthode pour obtenir la longueur et la .at(i)méthode pour accéder aux caractères :) Fiddle fonctionnalisé - ideone.com/tAZt5I
jave.web
2
Pour utiliser comme fonction: ideone.com/XOROjq - utilise<iostream> <algorithm> <cstring>
jave.web
36

Je souhaite supprimer les caractères "(", ")" et "-" de la chaîne.

Vous pouvez utiliser l' std::remove_if()algorithme pour supprimer uniquement les caractères que vous spécifiez:

#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
}

L' std::remove_if()algorithme nécessite quelque chose appelé un prédicat, qui peut être un pointeur de fonction comme l'extrait de code ci-dessus.

Vous pouvez également passer un objet fonction (un objet qui surcharge l' ()opérateur d' appel de fonction ). Cela nous permet de créer une solution encore plus générale:

#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
}

Vous pouvez spécifier les caractères à supprimer avec la "()- "chaîne. Dans l'exemple ci-dessus, j'ai ajouté un espace afin que les espaces soient supprimés ainsi que les parenthèses et les tirets.

In silico
la source
Vous pouvez également utiliserispunct(int c)
MSalters
Excellente mise en œuvre. Cette méthode a parfaitement fonctionné et a beaucoup de place pour une dynamique supplémentaire. Merci pour votre réponse. MSalters, je vais également rechercher la fonction ispunct (int c) et rendre compte de mon fonctionnement.
SD.
12

remove_if () a déjà été mentionné. Mais, avec C ++ 0x, vous pouvez spécifier le prédicat avec un lambda à la place.

Voici un exemple de cela avec 3 façons différentes de faire le filtrage. Les versions "copie" des fonctions sont également incluses pour les cas où vous travaillez avec un const ou que vous ne voulez pas modifier l'original.

#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;
}
Ombre2531
la source
Au lieu de const char & c, j'aurais dû vraiment utiliser const string :: value_type &. Mais ce n'est pas un gros problème dans ce cas.
Shadow2531
1
Il s'agit d'une mise en œuvre très approfondie. Je l'apprécie et j'utiliserai également cette implémentation.
SD.
8

Voici une solution différente pour toute personne intéressée. Il utilise la nouvelle plage For en 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;
ashwin911
la source
1
(1) l' str2initialisation n'est pas requise. (2) str = std::move(str2)serait plus efficace.
Ajay le
6

J'ai bien peur qu'il n'y ait pas un tel membre pour std :: string, mais vous pouvez facilement programmer ce genre de fonctions. Ce n'est peut-être pas la solution la plus rapide, mais cela suffirait:

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

EDIT: En lisant la réponse ci-dessous, j'ai compris qu'elle était plus générale, pas seulement pour détecter un chiffre. La solution ci-dessus omettra tous les caractères passés dans la deuxième chaîne d'argument. Par exemple:

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

Aura pour résultat

99999876543.87
StormByte
la source
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());

Remarque: il est possible que vous ayez besoin d'écrire ptr_fun<int, int>plutôt que de simplementptr_fun

Oleg Svechkarenko
la source
comment n'est-ce pas la réponse choisie?
user3240688
@ user3240688 Notez que std :: ptr_fun est obsolète dans C ++ 11 et sera supprimé dans C ++ 17 et std :: not1 est obsolète dans C ++ 17. Vous pouvez utiliser std::crefor std::function(ou lambdas).
Roi Danton
3

Oui, vous pouvez utiliser la fonction isdigit () pour vérifier les chiffres :)

Voici:

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

J'espère que ça aide :)

Vern
la source
Cela peut être modifié pour supprimer l'élément qui renvoie false. Je vous remercie.
SD.
3

boost::is_any_of

Supprimez tous les caractères d'une chaîne qui apparaissent dans une autre chaîne donnée:

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

Testé dans Ubuntu 16.04, Boost 1.58.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
2

Si vous avez accès à un compilateur prenant en charge les modèles variadiques, vous pouvez utiliser ceci:

#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;
}
Time Square
la source
1

Voici encore une autre alternative:

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, "()-");

Fonctionne avec std :: string et std :: wstring

Jem
la source
1

Je suis nouveau, mais certaines des réponses ci-dessus sont incroyablement compliquées, alors voici une alternative.

REMARQUE: tant que 0-9 sont contigus (ce qu'ils devraient être selon la norme), cela devrait filtrer tous les autres caractères sauf les nombres et «». Sachant que 0-9 doit être contigu et qu'un caractère est vraiment un int, nous pouvons faire ce qui suit.

EDIT: Je n'ai pas remarqué que l'affiche voulait aussi des espaces, alors je l'ai modifiée ...

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

Voici pour filtrer les caractères fournis.

#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;
}
Juste fatigué
la source
Cela ne fait pas ce que le PO voulait; il supprime également les espaces.
Andrew Barber
1

Utilisation de std :: wstring et wchar_t (nécessite l'en- tête Unicode ):

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

... l'initialiseur de plage statique fantaisie ensuite; pas nécessaire de configurer badChars2 exactement de la même manière. C'est exagéré; plus académique qu'autre chose:

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

Lambda simple et concis:

  1. Utilise le téléphone dans la liste de capture lambda.
  2. Utilise l' idiome Erase-Remove
  3. Supprime tous les mauvais caractères du téléphone

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

Sortie: "555 5555555"

Darrin
la source
1

Pour ceux d'entre vous qui préfèrent un style de codage lambda plus concis et plus facile à lire ...

Cet exemple supprime tous les caractères non alphanumériques et d'espaces blancs d'une chaîne large. Vous pouvez le mélanger avec l'un des autres ctype.h fonctions d'assistance pour supprimer des tests basés sur des caractères complexes.

(Je ne sais pas comment ces fonctions géreraient les langues CJK, alors marchez doucement.)

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

Voyez si vous ne trouvez pas cela plus facile à comprendre que le C / C ++ bruyant pour les boucles / 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 résultats après l'exécution de ce code: " 1ReplenMoveRPMV "

Ceci est juste académique, car il serait clairement plus précis, concis et efficace de combiner la logique 'if' de lambda0 (premier for_each ) dans le seul lambda1 (deuxième for_each ), si vous avez déjà établi quels caractères sont les "badChars" .

Darrin
la source
Merci à la réponse de @Eric Z pour avoir mentionné et utilisé l'idiome pratique Erase-remove. en.wikipedia.org/wiki/Erase-remove_idiom
Darrin
0

Beaucoup de bonnes réponses, voici une autre façon de nettoyer une chaîne de nombres, ne supprime pas les caractères mais en déplaçant les nombres.

string str("(555) 555-5555"), clean;
for (char c : str)
    if (c >= 48 and c <= 57)
        clean.push_back(c);
Patricio Rossi
la source