C ++で文字列から特定の文字を削除する方法は?


96

たとえば、ユーザーに電話番号を入力してもらいます。

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

文字列から「(」、「)」、「-」の文字を削除したい。文字列のremove、find、replace関数を確認しましたが、位置に基づいて動作することがわかりました。

文字「(」など)を渡し、文字列内のすべてのインスタンスを削除するために使用できる文字列関数はありますか?

回答:


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;

関数として使用するには:

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
これはどのように作動しますか?消去して削除するのはダブルネガティブではありませんか?私にはこれは次のように書かれています:「()-でない位置にある文字を消去してください。」そして、それぞれが一度に行われるので、すべての文字を削除してはいけませんか?私は両方の関数のドキュメントを読みましたが、これは私には意味がありません。cplusplus.com/reference/algorithm/remove cplusplus.com/reference/string/string/erase
ブレント

@Brent std :: remove()は、有効な文字を文字列から削除せず、有効な文字を一緒に移動するだけです。
lk_vc 2013

20
@Brentと将来の読者、これはErase-removeイディオムです。簡単に言うと、std::remove削除されていないアイテムをベクターの前に移動し、最後に削除されていないアイテムのすぐ後ろを指すイテレータを返します。次にstd::erase、そのイテレーターから最後までベクトルをトリムします。
chwarr 2013

1
私たちは使うべきだと思う本当にC ++バージョンについてはstring chars("()-");、その後、使用.length()長さと取得する方法を.at(i)-アクセスに文字をする方法を:) Functionizedフィドルideone.com/tAZt5I
jave.web

2
関数 として使用するにはideone.com/XOROjq-使用<iostream> <algorithm> <cstring>
jave.web

36

文字列から「(」、「)」、「-」の文字を削除したい。

std::remove_if()アルゴリズムを使用して、指定した文字のみを削除できます。

#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()このアルゴリズムは、上記のスニペットのような関数ポインタすることができ述語と呼ばれるものが必要です。

関数オブジェクト(関数呼び出し()演算子をオーバーロードするオブジェクト)を渡すこともできます。これにより、さらに一般的なソリューションを作成できます。

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

"()- "文字列で削除する文字を指定できます。上記の例では、スペースと括弧とダッシュが削除されるようにスペースを追加しました。


次も使用できますispunct(int c)
MSalters

優れた実装。この方法は完璧に機能し、さらなるダイナミクスの余地がたくさんあります。ご回答ありがとうございます。MSalters、ispunct(int c)関数も調べて、動作を報告します。
SD。

12

remove_if()はすでに言及されています。ただし、C ++ 0xでは、代わりにラムダを使用して述語を指定できます。

以下は、フィルタリングを行う3つの異なる方法の例です。関数の「コピー」バージョンは、constで作業している場合、または元の関数を変更したくない場合のためにも含まれています。

#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の代わりに、const string :: value_type&を実際に使用する必要がありました。しかし、この場合は大した問題ではありません。
Shadow2531

1
これは非常に徹底した実装です。私はそれを感謝し、この実装も使用します。
SD。

8

ここに興味のある人のための別の解決策があります。c ++ 11では新しいFor範囲を使用しています

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)str2初期化は必要ありません。(2)str = std::move(str2)より効率的です。
Ajay

6

std :: stringにはそのようなメンバーはないと思いますが、そのような関数は簡単にプログラムできます。これは最速のソリューションではないかもしれませんが、これで十分です:

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

編集:以下の答えを読んで、私はそれが数字を検出するだけでなく、より一般的であることを理解しました。上記のソリューションでは、2番目の引数文字列で渡されるすべての文字が省略されます。例えば:

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

結果として

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

注:ptr_fun<int, int>単純ではなく、書く必要がある可能性がありますptr_fun


これは選択された答えではないのですか?
user3240688 2017

@ user3240688注ことのstd :: ptr_funは C ++ 11で廃止され、C ++ 17とで削除される予定のstd :: NOT1は C ++ 17で廃止されました。std::crefまたはstd::function(またはラムダ)を使用できます。
Roi Danton、2018年

3

はい、isdigit()関数を使用して数字をチェックできます:)

どうぞ:

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

それが役に立てば幸い :)


これを変更して、falseを返す要素を削除できます。ありがとうございました。
SD。

3

boost::is_any_of

別の文字列に現れる1つの文字列からすべての文字を取り除く:

#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でテスト済み。


2

可変個のテンプレートをサポートするコンパイラにアクセスできる場合は、これを使用できます。

#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

さらに別の方法を次に示します。

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およびstd :: wstringで動作します


1

私は新しいですが、上記の答えのいくつかはめちゃくちゃ複雑なので、ここに代替があります。

注:0〜9が連続している限り(標準に従っている必要があります)、これにより、数字と ''以外のすべての文字が除外されます。0〜9は連続している必要があり、charは本当にintであることがわかっているので、以下を実行できます。

編集:私もポスターがスペースを必要としていることに気づかなかったので、それを変更しました...

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

以下は、提供された文字をフィルタリングするためのものです。

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

それはOPが望んだことをしません。スペースも削除されます。
Andrew Barber

1

std :: wstringwchar_tを使用する(Unicodeヘッダーが必要):

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

...次は空想の静的範囲初期化子です。これとまったく同じ方法でbadChars2を設定する必要はありません。それはやり過ぎです。何よりも学術的:

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

シンプルで簡潔なラムダ:

  1. ラムダキャプチャリストで電話を使用します。
  2. Erase-removeイディオムを使用
  3. 電話からすべての悪い文字を削除します

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

出力:「555 5555555」


1

より簡潔で読みやすいラムダコーディングスタイルを好む方のために...

この例では、ワイド文字列から英数字以外の空白文字をすべて削除します。これを他のctype.hヘルパー関数と混同して、複雑に見える文字ベースのテストを削除できます。

(これらの関数がCJK言語をどのように処理するかわからないので、そっと歩いてください。)

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

これが、ノイズの多い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の結果: " 1ReplenMoveRPMV "

「badChars」である文字をすでに確立している場合は、lambda0(最初のfor_each)の「if」ロジックを単一のlambda1(2番目のfor_each)に組み合わせる方が明らかにより正確で簡潔で効率的であるため、これは単なる学術的です。 。


便利なErase-removeイディオムについて言及し、使用した@Eric Zの回答に感謝します。en.wikipedia.org/wiki/Erase-remove_idiom
Darrin

0

良い答えがたくさんあります。これは、数字の文字列をクリーンアップする別の方法です。文字を削除するのではなく、数字を移動することです。

string str("(555) 555-5555"), clean;
for (char c : str)
    if (c >= 48 and c <= 57)
        clean.push_back(c);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.