Como remover certos caracteres de uma string em C ++?


96

Por exemplo, eu tenho um usuário que insere um número de telefone.

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

Desejo remover os caracteres "(", ")" e "-" da string. Eu olhei para as funções de remoção, localização e substituição de strings, no entanto, só vejo que elas operam com base na posição.

Existe uma função de string que posso usar para passar um caractere, "(" por exemplo, e fazer com que ele remova todas as instâncias de uma string?

Respostas:


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 função :

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
Como é que isso funciona? Não é um duplo negativo usar apagar e remover? Para mim, isso diz: "apague os caracteres que estão nas posições onde () - não estão." E uma vez que cada um é feito por vez, não deveria remover TODOS os caracteres? Eu li a documentação sobre as duas funções e isso não faz sentido para mim. cplusplus.com/reference/algorithm/remove cplusplus.com/reference/string/string/erase
Brent de

@Brent std :: remove () NÃO removerá quaisquer caracteres válidos da string, apenas move os caracteres válidos juntos.
lk_vc

20
@Brent e futuros leitores, este é o idioma Erase-remove . Resumidamente, std::removemove os itens não removidos para a frente do vetor e retorna um iterador apontando logo além do último item não removido. Em seguida, std::erasecorta o vetor desse iterador até o final.
chwarr

1
Para realmente a versão C ++, acho que devemos usar string chars("()-");e, em seguida, usar o .length()método para obter o comprimento e o .at(i)método para acessar os caracteres :) Violino funcionalizado - ideone.com/tAZt5I
jave.web

2
Para usar como função: ideone.com/XOROjq - usa<iostream> <algorithm> <cstring>
jave.web

36

Desejo remover os caracteres "(", ")" e "-" da string.

Você pode usar o std::remove_if()algoritmo para remover apenas os caracteres que especificar:

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

O std::remove_if()algoritmo requer algo chamado predicado, que pode ser um ponteiro de função como o snippet acima.

Você também pode passar um objeto de função (um objeto que sobrecarrega o ()operador de chamada de função ). Isso nos permite criar uma solução ainda mais geral:

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

Você pode especificar quais caracteres remover com a "()- "string. No exemplo acima, adicionei um espaço para que os espaços sejam removidos, bem como parênteses e travessões.


Você também pode usarispunct(int c)
MSalters

Excelente implementação. Este método funcionou perfeitamente e tem muito espaço para novas dinâmicas. Obrigado pela resposta. MSalters, também irei pesquisar a função ispunct (int c) e relatar sobre o meu funcionamento.
SD.

12

remove_if () já foi mencionado. Mas, com C ++ 0x, você pode especificar o predicado para ele com um lambda.

Abaixo está um exemplo disso com 3 maneiras diferentes de fazer a filtragem. Versões de "cópia" das funções também estão incluídas para os casos em que você está trabalhando com um const ou não deseja modificar o 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;
}

Em vez de const char & c, eu deveria realmente ter usado const string :: value_type &. Mas, não é grande coisa neste caso.
Shadow2531

1
Esta é uma implementação muito completa. Agradeço e usarei essa implementação também.
SD.

8

Aqui está uma solução diferente para todos os interessados. Ele usa o novo intervalo For em 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) a str2inicialização não é necessária. (2) str = std::move(str2)seria mais eficiente.
Ajay

6

Infelizmente, não existe tal membro para std :: string, mas você pode facilmente programar esse tipo de funções. Pode não ser a solução mais rápida, mas bastaria:

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: Lendo a resposta abaixo, entendi que fosse mais geral, não apenas para detectar dígito. A solução acima irá omitir todos os caracteres passados ​​na segunda string de argumento. Por exemplo:

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

Vai resultar em

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

Nota: É possível que você precise escrever ptr_fun<int, int>ao invés de simplesptr_fun


como esta não é a resposta selecionada?
user3240688

@ user3240688 Observe que std :: ptr_fun está obsoleto no C ++ 11 e será removido no C ++ 17 e std :: not1 está obsoleto no C ++ 17. Você pode usar std::crefou std::function(ou lambdas).
Roi Danton

3

Sim, você pode usar a função isdigit () para verificar se há dígitos :)

Aqui está:

#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 que ajude :)


Isso pode ser modificado para remover o elemento que retorna falso. Obrigado.
SD.

3

boost::is_any_of

Retire todos os caracteres de uma string que aparecem em outra 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"));
}

Testado no Ubuntu 16.04, Boost 1.58.


2

Se você tiver acesso a um compilador que ofereça suporte a modelos variados, poderá usar este:

#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

Aqui está outra alternativa:

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 com std :: string e std :: wstring


1

Eu sou novo, mas algumas das respostas acima são extremamente complicadas, então aqui está uma alternativa.

NOTA: Desde que 0-9 sejam contíguos (o que deve ser de acordo com o padrão), isso deve filtrar todos os outros caracteres, exceto números e ''. Sabendo que 0-9 deve ser contíguo e um char é realmente um int, podemos fazer o seguinte.

EDIT: Eu não percebi que o pôster queria espaços também, então eu o alterei ...

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

Abaixo é para filtrar os caracteres fornecidos.

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

Isso não faz o que o OP queria; ele exclui os espaços também.
Andrew Barber de

1

Usando std :: wstring e wchar_t (requer o cabeçalho Unicode ):

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

... próximo inicializador de intervalo estático sofisticado; não é necessário configurar badChars2 exatamente da mesma maneira. É um exagero; mais acadêmico do que qualquer outra coisa:

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

Lambda simples e conciso:

  1. Usa telefone na lista de captura de lambda.
  2. Usa o idioma Apagar-Remover
  3. Remove todos os caracteres ruins do telefone

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

Resultado: "555 5555555"


1

Para aqueles que preferem um estilo de codificação lambda mais conciso e fácil de ler ...

Este exemplo remove todos os caracteres não alfanuméricos e de espaço em branco de uma string larga. Você pode misturá-lo com qualquer um dos outros ctype.h funções auxiliares para remover testes baseados em caracteres de aparência complexa.

(Não tenho certeza de como essas funções lidariam com as linguagens CJK, então vá com cuidado.)

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

Veja se você não acha isso mais fácil de entender do que loops barulhentos em C / C ++ para / iterador:

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 resulta após executar este código: " 1ReplenMoveRPMV "

Este é apenas acadêmica, uma vez que seria claramente mais precisa, concisa e eficiente para combinar a lógica 'se' a partir lambda0 (primeira for_each ) no único lambda1 (segunda for_each ), se você já estabeleceu que os personagens são os "badChars" .


Agradecemos a resposta de @Eric Z por mencionar e usar o prático idioma Erase-remove. en.wikipedia.org/wiki/Erase-remove_idiom
Darrin

0

Muitas respostas boas, aqui está outra maneira de limpar uma sequência de números, não é deletar caracteres, mas mover os números para fora.

string str("(555) 555-5555"), clean;
for (char c : str)
    if (c >= 48 and c <= 57)
        clean.push_back(c);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.