110

For example I have a user input a phone number.

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

I want to remove the "(", ")", and "-" characters from the string. I've looked at the string remove, find and replace functions however I only see that they operate based on position.

Is there a string function that I can use to pass a character, "(" for example, and have it remove all instances within a string?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
SD.
  • 3,089
  • 10
  • 26
  • 21

15 Answers15

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

To use as function:

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, "()-" );
jave.web
  • 13,880
  • 12
  • 91
  • 125
Eric Z
  • 14,327
  • 7
  • 45
  • 69
  • 6
    How does this work? Is it not a double negative to use erase and remove? To me this reads: "erase the characters that are in positions where ()- aren't." And since each one is done at a time, shouldn't it remove ALL characters? I've read the documentation on both functions, and this makes no sense to me. http://www.cplusplus.com/reference/algorithm/remove/ http://www.cplusplus.com/reference/string/string/erase/ – Brent Apr 10 '13 at 00:35
  • @Brent std::remove() will NOT remove any valid characters from the string, it just moves valid characters together. – lk_vc Apr 11 '13 at 12:04
  • 24
    @Brent and future readers, this is the [Erase-remove idiom](http://en.wikipedia.org/wiki/Erase-remove_idiom). Briefly, `std::remove` moves the non-removed items to the front of the vector and returns an iterator pointing just beyond the last unremoved item. Then `std::erase` trims the vector from that iterator to the end. – chwarr Dec 02 '13 at 11:01
  • 1
    For really C++ version I think we should use `string chars("()-");` and then use `.length()` method to get the length and `.at(i)` method to access the chars :) Functionized fiddle - http://ideone.com/tAZt5I – jave.web Mar 03 '15 at 05:35
  • 2
    To use as **function:** http://ideone.com/XOROjq - uses `` `` `` – jave.web Apr 02 '15 at 04:41
  • 2
    you'd better cache `strlen(chars)` cause it has `O(n)` complexity – fnc12 Apr 20 '17 at 13:04
  • I've always been confused about what str.end() as a parameter of str.erase does. I get memory problems if i exclude str.end() though... – Goku Sep 28 '18 at 03:47
  • there's no guarantee that `unsigned int` can hold the number of characters in chars; strlen returns size_t, not int, for a reason. maybe the chars list is over 4GB, and unsigned int is uint32_t ? replace `unsigned int` with size_t ; and listen to @fnc12 , that code should use sizeof, not strlen, for performance reasons. (or if you want to use strlen, the strlen should be cached.) – hanshenrik Apr 27 '21 at 09:01
  • @Goku erase() is overloaded, and the three variants of it have very different semantics, as I discovered the hard way. The variant that takes a single iterator (which is what's called if that str.end() argument is omitted), erases just the character at the position specified by that iterator. If remove() happens to return str.end(), which is what happens if remove() doesn't find any matching characters, bad things happen, at least in some environments, as erase() will try to erase a character beyond the end of the string. – Gerry Beauregard Nov 03 '21 at 01:01
42

I want to remove the "(", ")", and "-" characters from the string.

You can use the std::remove_if() algorithm to remove only the characters you specify:

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

The std::remove_if() algorithm requires something called a predicate, which can be a function pointer like the snippet above.

You can also pass a function object (an object that overloads the function call () operator). This allows us to create an even more general solution:

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

You can specify what characters to remove with the "()- " string. In the example above I added a space so that spaces are removed as well as parentheses and dashes.

In silico
  • 51,091
  • 10
  • 150
  • 143
  • You could also use `ispunct(int c)` – MSalters May 05 '11 at 07:21
  • Excellent implementation. This method worked flawlessly and has a lot of room for further dynamics. Thank you for the response. MSalters, I will also look up the ispunct(int c) function and report back on my workings. – SD. May 05 '11 at 17:28
14

remove_if() has already been mentioned. But, with C++0x, you can specify the predicate for it with a lambda instead.

Below is an example of that with 3 different ways of doing the filtering. "copy" versions of the functions are included too for cases when you're working with a const or don't want to modify the 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;
}
Shadow2531
  • 11,980
  • 5
  • 35
  • 48
  • Instead of const char& c, I should have really used const string::value_type&. But, it's not a big deal in this case. – Shadow2531 May 05 '11 at 09:29
  • 1
    This is a very thorough implementation. I appreciate it and will use this implementation as well. – SD. May 05 '11 at 17:37
9

Here is a different solution for anyone interested. It uses the new For range in 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
  • 91
  • 1
  • 2
  • 1
    (1) ``str2`` initialization is not required. (2) ``str = std::move(str2)`` would be more efficient. – Ajay Jun 04 '19 at 18:51
7

I'm afraid there is no such a member for std::string, but you can easily program that kind of functions. It may not be the fastest solution but this would suffice:

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: Reading the answer below, I understood it to be more general, not only to detect digit. The above solution will omit every character passed in the second argument string. For example:

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

Will result in

99999876543.87
StormByte
  • 1,266
  • 1
  • 13
  • 33
5

boost::is_any_of

Strip for all characters from one string that appear in another given string:

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

Tested in Ubuntu 16.04, Boost 1.58.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
4
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());

Note: It's posible you need write ptr_fun<int, int> rather than simple ptr_fun

Oleg Svechkarenko
  • 2,508
  • 25
  • 30
  • how is this not the selected answer? – user3240688 Dec 20 '17 at 06:49
  • @user3240688 Note that [std::ptr_fun](http://en.cppreference.com/w/cpp/utility/functional/ptr_fun) is deprecated in C++11 and will be removed in C++17 and [std::not1](http://en.cppreference.com/w/cpp/utility/functional/not1) is deprecated in C++17. You could use `std::cref` or `std::function` (or lambdas). – Roi Danton May 04 '18 at 11:29
4

Yes, you can use the isdigit() function to check for a digits :)

Here you go:

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

Hope it helps :)

Community
  • 1
  • 1
Vern
  • 2,393
  • 1
  • 15
  • 18
3

If you have access to a compiler that supports variadic templates, you can use this:

#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;
}
Timesquare
  • 399
  • 2
  • 12
2

I'm new, but some of the answers above are insanely complicated, so here's an alternative.

NOTE: As long as 0-9 are contiguous (which they should be according to the standard), this should filter out all other characters but numbers and ' '. Knowing 0-9 should be contiguous and a char is really an int, we can do the below.

EDIT: I didn't notice the poster wanted spaces too, so I altered it...

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

Below is to filter supplied characters.

#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;
}
JustTired
  • 141
  • 9
2

For those of you that prefer a more concise, easier to read lambda coding style...

This example removes all non-alphanumeric and white space characters from a wide string. You can mix it up with any of the other ctype.h helper functions to remove complex-looking character-based tests.

(I'm not sure how these functions would handle CJK languages, so walk softly there.)

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

See if you don't find this easier to understand than noisy C/C++ for/iterator loops:

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 results after running this code: "1ReplenMoveRPMV"

This is just academic, since it would clearly be more precise, concise and efficient to combine the 'if' logic from lambda0 (first for_each) into the single lambda1 (second for_each), if you have already established which characters are the "badChars".

Darrin
  • 317
  • 1
  • 4
  • 10
  • Credit to @Eric Z's answer for mentioning and using the handy Erase-remove idiom. http://en.wikipedia.org/wiki/Erase-remove_idiom – Darrin Jan 09 '14 at 08:00
2

Using std::wstring and wchar_t (requires the Unicode header):

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

...fancy static range initializer next; not necessary to setup badChars2 this exact same way. It's overkill; more academic than anything else:

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

Simple, concise lambda:

  1. Uses phone in the lambda capture list.
  2. Uses Erase-remove idiom
  3. Removes all bad characters from 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;
    

Output: "555 5555555"

Darrin
  • 317
  • 1
  • 4
  • 10
2

Here's yet another 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, "()-");

Works with std::string and std::wstring

Jem
  • 2,255
  • 18
  • 25
1

Lot of good answers, here is another way to clean up a string of numbers, is not deleting chars but by moving the numbers out.

string str("(555) 555-5555"), clean;
for (char c : str)
    if (c >= 48 and c <= 57)
        clean.push_back(c);
Patricio Rossi
  • 167
  • 2
  • 5
-1

From C++20, you can use erase/erase_if for std::basic_string, which is basically a convenience wrapper for the erase-remove idiom

std::erase(phone, '('); 

and

std::erase_if(phone, [](char x) { 
                        return x == '(' or x == ')' or x == '-'; 
                     });

Note that these functions return the count of how many characters were erased as well.

cigien
  • 57,834
  • 11
  • 73
  • 112