C ++ 'da bir dizeden belirli karakterler nasıl kaldırılır?


96

Örneğin, bir kullanıcının bir telefon numarası girmesi gerekiyor.

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

Dizeden "(", ")" ve "-" karakterlerini kaldırmak istiyorum. Dizgeyi kaldır, bul ve değiştir işlevlerine baktım, ancak yalnızca konuma göre çalıştıklarını görüyorum.

Örneğin, "(" karakterini geçirmek için kullanabileceğim ve bir dizedeki tüm örnekleri kaldırmasını sağlayan bir dize işlevi var mı?

Yanıtlar:


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;

İşlev olarak kullanmak için :

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
Bu nasıl çalışıyor? Silme ve silme işlevini kullanmak çifte olumsuz değil mi? Bana göre bu: "() - olmadığı konumlardaki karakterleri silin." Ve her biri bir seferde yapıldığından, TÜM karakterleri kaldırması gerekmez mi? Her iki fonksiyonla ilgili belgeleri okudum ve bu bana mantıklı gelmiyor. cplusplus.com/reference/algorithm/remove cplusplus.com/reference/string/string/erase
Brent

@Brent std :: remove () dizeden herhangi bir geçerli karakteri KALDIRMAZ, sadece geçerli karakterleri birlikte taşır.
lk_vc

21
@Brent ve gelecekteki okuyucular, bu Sil-kaldır deyimidir . Kısaca, std::removekaldırılmamış öğeleri vektörün önüne taşır ve kaldırılmamış son öğenin hemen ötesini gösteren bir yineleyici döndürür. Sonra std::erasevektörü bu yineleyiciden sonuna kadar kırpar.
chwarr

1
Gerçekten C ++ sürümü için, karakterlere erişmek için uzunluğu ve yöntemi elde etmek için yöntemi kullanmalı string chars("()-");ve sonra kullanmalıyız :) İşlevsel keman - ideone.com/tAZt5I.length().at(i)
jave.web

2
İşlev olarak kullanmak için : ideone.com/XOROjq - kullanır<iostream> <algorithm> <cstring>
jave.web

36

Dizeden "(", ")" ve "-" karakterlerini kaldırmak istiyorum.

Sen kullanabilirsiniz std::remove_if()Yalnızca belirttiğiniz karakterleri kaldırmak için algoritma:

#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()Algoritmanın yukarıda pasajı gibi işlev işaretçisi olabilir mesnet olarak adlandırılan şey gerektirir.

Ayrıca bir işlev nesnesi (işlev çağrısı ()operatörünü aşırı yükleyen bir nesne) da iletebilirsiniz . Bu, daha genel bir çözüm oluşturmamızı sağlar:

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

Dizeyle hangi karakterlerin kaldırılacağını belirtebilirsiniz "()- ". Yukarıdaki örnekte, parantezlerin ve tirelerin yanı sıra boşlukların da kaldırılması için bir boşluk ekledim.


Ayrıca şunu da kullanabilirsinizispunct(int c)
MSalters

Mükemmel uygulama. Bu yöntem kusursuz bir şekilde çalıştı ve daha fazla dinamik için çok yer var. Cevap için teşekkürler. MSalters, ayrıca ispunct (int c) işlevine bakacağım ve çalışmalarım hakkında geri bildirimde bulunacağım.
SD.

12

remove_if () daha önce bahsedilmiş. Ancak, C ++ 0x ile bunun yerine bir lambda ile bunun yüklemini belirtebilirsiniz.

Aşağıda, filtrelemenin 3 farklı yolu ile bir örnek verilmiştir. Bir const ile çalıştığınız veya orijinali değiştirmek istemediğiniz durumlar için işlevlerin "kopya" sürümleri de dahil edilir.

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

Const char & c yerine, gerçekten const string :: value_type &. Ancak, bu durumda çok önemli değil.
Shadow2531

1
Bu çok kapsamlı bir uygulama. Bunu takdir ediyorum ve bu uygulamayı da kullanacağım.
SD.

8

İşte ilgilenen herkes için farklı bir çözüm. C ++ 11'de yeni For aralığını kullanır

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) str2başlatma gerekli değildir. (2) str = std::move(str2)daha verimli olacaktır.
Ajay

6

Korkarım std :: string için böyle bir üye yok, ancak bu tür işlevleri kolayca programlayabilirsiniz. En hızlı çözüm olmayabilir, ancak bu yeterli olacaktır:

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

DÜZENLEME: Aşağıdaki cevabı okurken, sadece rakamları tespit etmek için değil, daha genel olduğunu anladım. Yukarıdaki çözüm, ikinci bağımsız değişken dizesinde geçirilen her karakteri atlayacaktır. Örneğin:

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

Sonuçlanacak

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

Not: Basitten ptr_fun<int, int>çok yazmaya ihtiyacınız olabilirptr_fun


bu nasıl seçilen cevap değil?
user3240688

@ user3240688 std :: ptr_fun'un C ++ 11'de kaldırıldığını ve C ++ 17'de kaldırılacağını ve std :: not1'in C ++ 17'de kullanımdan kaldırıldığını unutmayın. Sen kullanabilirsiniz std::crefveya std::function(veya Lambda'lar).
Roi Danton

3

Evet, rakamları kontrol etmek için isdigit () işlevini kullanabilirsiniz :)

Hadi bakalım:

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

Umarım yardımcı olur :)


Bu, false döndüren öğeyi kaldırmak için değiştirilebilir. Teşekkür ederim.
SD.

3

boost::is_any_of

Verilen başka bir dizede görünen bir dizedeki tüm karakterler için şeritleyin:

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

Ubuntu 16.04, Boost 1.58'de test edilmiştir.


2

Değişken şablonları destekleyen bir derleyiciye erişiminiz varsa, bunu kullanabilirsiniz:

#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

İşte başka bir alternatif:

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

Std :: string ve std :: wstring ile çalışır


1

Yeniyim, ancak yukarıdaki cevaplardan bazıları delice karmaşık, bu yüzden işte bir alternatif.

NOT: 0-9 bitişik olduğu sürece (standarda göre olmaları gerekir), bu, sayılar ve '' dışındaki tüm diğer karakterleri filtrelemelidir. 0-9'u bilmek bitişik olmalıdır ve bir karakter gerçekten bir int'dir, aşağıdakileri yapabiliriz.

DÜZENLEME: Posterin de boşluk istediğini fark etmedim, bu yüzden değiştirdim ...

#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şağıda sağlanan karakterleri filtrelemek için var.

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

Bu, OP'nin istediğini yapmaz; boşlukları da siler.
Andrew Barber

1

Kullanılması std :: wstring ve wchar_t (gerektirir Unicode başlığını):

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

... sonra fantezi statik aralık başlatıcı; badChars2'yi aynı şekilde kurmak gerekli değildir. Bu aşırıdır; her şeyden daha akademik:

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

Basit, özlü lambda:

  1. Lambda yakalama listesinde telefonu kullanır .
  2. Sil-kaldır deyimi kullanır
  3. Tüm kötü karakterleri telefondan kaldırır

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

Çıktı: "555 5555555"


1

Daha özlü, okunması daha kolay lambda kodlama stilini tercih edenler için ...

Bu örnek, geniş bir dizeden tüm alfanümerik olmayan ve beyaz boşluk karakterlerini kaldırır. Karmaşık görünümlü karakter tabanlı testleri kaldırmak için diğer ctype.h yardımcı işlevlerinden herhangi biriyle karıştırabilirsiniz .

(Bu işlevlerin CJK dillerini nasıl işleyeceğinden emin değilim, bu yüzden yavaşça yürüyün.)

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

Bunu anlamak için gürültülü C / C ++ for / yineleyici döngülerinden daha kolay bulup bulmadığınıza bakın:

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

Bu kodu çalıştırdıktan sonra newLabel sonuçları: " 1ReplenMoveRPMV "

Bu açıkça lambda0 (ilk gelen 'eğer' mantığı birleştirmek daha hassas özlü ve verimli olacaktır çünkü sadece akademik bir for_each tek lambda1 (ikinci içine) for_each zaten kurduk eğer "badChars" hangi karakterler,) .


Kullanışlı Erase-remove deyiminden bahsetmek ve kullanmak için @Eric Z'nin cevabına kredi verin. en.wikipedia.org/wiki/Erase-remove_idiom
Darrin

0

Pek çok iyi cevap, işte bir dizi sayıyı temizlemenin başka bir yolu, karakterleri silmek değil, sayıları dışarı taşımaktır.

string str("(555) 555-5555"), clean;
for (char c : str)
    if (c >= 48 and c <= 57)
        clean.push_back(c);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.