¿Cómo eliminar ciertos caracteres de una cadena en C ++?

96

Por ejemplo, un usuario ingresa un número de teléfono.

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

Quiero eliminar los caracteres "(", ")" y "-" de la cadena. He examinado las funciones de eliminación, búsqueda y reemplazo de cadenas, sin embargo, solo veo que funcionan según la posición.

¿Existe una función de cadena que pueda usar para pasar un carácter, "(" por ejemplo, y hacer que elimine todas las instancias dentro de una cadena?

DAKOTA DEL SUR.
fuente

Respuestas:

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;

Para usar como función :

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
fuente
4
¿Como funciona esto? ¿No es un doble negativo usar borrar y eliminar? Para mí, esto dice: "borre los caracteres que están en posiciones donde () - no lo están". Y dado que cada uno se hace a la vez, ¿no debería eliminar TODOS los caracteres? He leído la documentación sobre ambas funciones y esto no tiene sentido para mí. cplusplus.com/reference/algorithm/remove cplusplus.com/reference/string/string/erase
Brent
@Brent std :: remove () NO eliminará ningún carácter válido de la cadena, solo mueve los caracteres válidos juntos.
lk_vc
20
@Brent y futuros lectores, este es el modismo Erase-remove . Brevemente, std::removemueve los elementos no eliminados al frente del vector y devuelve un iterador que apunta justo más allá del último elemento no eliminado. Luego std::eraserecorta el vector desde ese iterador hasta el final.
chwarr
1
Para la versión realmente C ++, creo que deberíamos usar string chars("()-");y luego usar el .length()método para obtener la longitud y el .at(i)método para acceder a los caracteres :) Violín
jave.web
2
Para usar como función: ideone.com/XOROjq - uses<iostream> <algorithm> <cstring>
jave.web
36

Quiero eliminar los caracteres "(", ")" y "-" de la cadena.

Puede utilizar el std::remove_if()algoritmo para eliminar solo los caracteres que especifique:

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

El std::remove_if()algoritmo requiere algo llamado predicado, que puede ser un puntero de función como el fragmento de arriba.

También puede pasar un objeto de función (un objeto que sobrecarga el ()operador de llamada de función ). Esto nos permite crear una solución aún más general:

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

Puede especificar qué caracteres eliminar con la "()- "cadena. En el ejemplo anterior, agregué un espacio para que se eliminen los espacios, así como los paréntesis y los guiones.

En silico
fuente
También puede utilizarispunct(int c)
MSalters
Excelente implementación. Este método funcionó a la perfección y tiene mucho espacio para más dinámicas. Gracias por la respuesta. MSalters, también buscaré la función ispunct (int c) e informaré sobre mi funcionamiento.
SD.
12

remove_if () ya se ha mencionado. Pero, con C ++ 0x, puede especificar el predicado con una lambda en su lugar.

A continuación se muestra un ejemplo de eso con 3 formas diferentes de hacer el filtrado. También se incluyen versiones de "copia" de las funciones para los casos en los que está trabajando con una constante o no desea modificar el 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;
}
Sombra2531
fuente
En lugar de const char & c, debería haber usado realmente const string :: value_type &. Pero no es gran cosa en este caso.
Shadow2531
1
Esta es una implementación muy completa. Se lo agradezco y también utilizaré esta implementación.
SD.
8

Aquí hay una solución diferente para cualquier persona interesada. Utiliza el nuevo rango 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
fuente
1
(1) str2no se requiere inicialización. (2) str = std::move(str2)sería más eficiente.
Ajay
6

Me temo que no existe tal miembro para std :: string, pero puede programar fácilmente ese tipo de funciones. Puede que no sea la solución más rápida, pero esto sería suficiente:

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

EDITAR: Al leer la respuesta a continuación, entendí que era más general, no solo para detectar dígitos. La solución anterior omitirá todos los caracteres pasados ​​en la segunda cadena de argumentos. Por ejemplo:

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

Resultará en

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

Nota: es posible que necesite escribir en ptr_fun<int, int>lugar de simpleptr_fun

Oleg Svechkarenko
fuente
¿cómo no es esta la respuesta seleccionada?
user3240688
@ user3240688 Tenga en cuenta que std :: ptr_fun está en desuso en C ++ 11 y se eliminará en C ++ 17 y std :: not1 está en desuso en C ++ 17. Puede usar std::crefo std::function(o lambdas).
Roi Danton
3

Sí, puede utilizar la función isdigit () para comprobar si hay dígitos :)

Aqui tienes:

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

Espero eso ayude :)

Vern
fuente
Esto se puede modificar para eliminar el elemento que devuelve falso. Gracias.
SD.
3

boost::is_any_of

Elimine todos los caracteres de una cadena que aparecen en otra cadena determinada:

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

Probado en Ubuntu 16.04, Boost 1.58.

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

Si tiene acceso a un compilador que admita plantillas variadas, puede usar esto:

#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
fuente
1

Aquí hay otra alternativa más:

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

Funciona con std :: string y std :: wstring

Jem
fuente
1

Soy nuevo, pero algunas de las respuestas anteriores son increíblemente complicadas, así que aquí hay una alternativa.

NOTA: Siempre que 0-9 sean contiguos (que deberían ser de acuerdo con el estándar), esto debería filtrar todos los demás caracteres, excepto los números y ''. Sabiendo que 0-9 debe ser contiguo y un char es realmente un int, podemos hacer lo siguiente.

EDITAR: No noté que el póster también quería espacios, así que lo alteré ...

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

A continuación se muestra para filtrar los caracteres proporcionados.

#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;
}
Solo cansado
fuente
Eso no hace lo que quería el OP; también borra los espacios.
Andrew Barber
1

Usando std :: wstring y wchar_t (requiere el encabezado Unicode ):

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

... inicializador de rango estático elegante siguiente; no es necesario configurar badChars2 exactamente de la misma manera. Es una exageración; más académico que cualquier otra cosa:

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

Lambda simple y conciso:

  1. Utiliza el teléfono en la lista de captura lambda.
  2. Utiliza el modismo Borrar-eliminar
  3. Elimina todos los personajes malos del teléfono

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

Salida: "555 5555555"

Darrin
fuente
1

Para aquellos de ustedes que prefieren un estilo de codificación lambda más conciso y fácil de leer ...

Este ejemplo elimina todos los caracteres de espacio en blanco y no alfanuméricos de una cadena ancha. Puede mezclarlo con cualquiera de las otras funciones auxiliares de ctype.h para eliminar pruebas basadas en caracteres de aspecto complejo.

(No estoy seguro de cómo estas funciones manejarían los lenguajes CJK, así que camine suavemente allí).

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

Vea si no encuentra esto más fácil de entender que los ruidosos bucles 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 resultados después de ejecutar este código: " 1ReplenMoveRPMV "

Esto es solo académico, ya que claramente sería más preciso, conciso y eficiente combinar la lógica 'if' de lambda0 (primero para_cada ) en la única lambda1 (segundo para_cada ), si ya ha establecido qué caracteres son los "badChars" .

Darrin
fuente
Crédito a la respuesta de @Eric Z por mencionar y usar el práctico lenguaje Erase-remove. en.wikipedia.org/wiki/Erase-remove_idiom
Darrin
0

Muchas buenas respuestas, aquí hay otra forma de limpiar una serie de números, no eliminando caracteres, sino moviendo los números.

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