標準の文字列を検索/検索して置換するにはどうすればよいですか?


回答:


74

独自の置換を実装しないのはなぜですか?

void myReplace(std::string& str,
               const std::string& oldStr,
               const std::string& newStr)
{
  std::string::size_type pos = 0u;
  while((pos = str.find(oldStr, pos)) != std::string::npos){
     str.replace(pos, oldStr.length(), newStr);
     pos += newStr.length();
  }
}

3
ここで、「replace」へのすべての呼び出しでメモリを少し混乱させています。「ooooooo ... o」から「o」を削除すると、複雑さがn²になります。もっと上手くできると思いますが、この解決策は理解しやすいというメリットがあります。
Zonko、2011

1
なぜこれが難読化されたforループではなく、実際のforループではないのですか?
Shirik、

私は「最小の驚き」の原則を適用するのに慣れています。forループは、ほとんどの場合、単純なインデックスのインクリメント用です。ここで、私によれば、whileループはより明確です。
yves Baumes、

1
@aldo原則として、複雑さを避け、たとえば、他の返信で述べられているように正規表現を使用することをお勧めします。ただし、必要に応じて、プロジェクトの依存関係を制御することもできます。必要なものを正確に実行する小さなコードスニペットは、それ以上ではない場合があります。
yves Baumes 2012年

158
#include <boost/algorithm/string.hpp> // include Boost, a C++ library
...
std::string target("Would you like a foo of chocolate. Two foos of chocolate?");
boost::replace_all(target, "foo", "bar");

ここで公式ドキュメント replace_allには。


1
パターンと置換のためにstd :: stringを明示的に作成する必要がないことに注意してください:boost :: replace_all(target、 "foo"、 "bar");
Alexis Wilke、2011

4
+1、replace_allただし注意:バージョン12.3未満のSun Studioでブースト> 1.43のバージョンを超えるとsegfault
ブライアンヴァンデンバーグ

3
boost組み込みデバイスでのコンパイル時間を大幅に増やします。ARMv7クアッドコアでも。100行のコードは、ブーストなしで2秒で2分でコンパイルされます。
Piotr Kula

4
@ppumkin:コンパイラ(またはビルドセットアップなど)は、ターゲットアーキテクチャではなく、それとは何の関係もないことを意味します。
Daniel Kamil Kozar

コンパイラがプリコンパイル済みヘッダーをサポートしている場合は、boostを使用するときにそれを使用することを強くお勧めします。それは本当に時間を節約します。
Alexey Omelchenko

33

C ++ 11では、次の呼び出しを使用してこれをワンライナーとして実行できますregex_replace

#include <string>
#include <regex>

using std::string;

string do_replace( string const & in, string const & from, string const & to )
{
  return std::regex_replace( in, std::regex(from), to );
}

string test = "Remove all spaces";
std::cout << do_replace(test, " ", "") << std::endl;

出力:

Removeallspaces

ありがとう、非常に使いやすく覚えています!
Julian Declercq 2016年

またfrom、正規表現にすることもできます。必要に応じて、より高度な一致基準を使用できます。私が理解していないのは、なんらかの正規表現解析適用せずにこれを行う方法fromです。代わりに、文字の直接解釈のみを使用します。
ブレントブラッドバーン2016年

これには、最新のコンパイラが必要な場合があります。gcc 5.0で動作しましたが、gcc 4.8.4で問題が発生しました。
ブレントブラッドバーン2016年

@nobar、ええ、4.8.xでの正規表現のサポートが完全に覚えていなかった場合、そうではありませんでした。また、より洗練された検索を行うこともできますが、時間に関してペナルティが課せられます...それは、他のより直接的な検索および置換機能よりも遅くなります。
Alexis Wilke

2
これは非常に基本的な英数字にのみ機能し、文字列の種類に応じて多くの前処理を行わなければ機能しないことに注意してください。汎用の正規表現ベースの文字列置換はまだ見つかりません。
Piyush Soni、2016年

17

変更された文字列を返さないのはなぜですか?

std::string ReplaceString(std::string subject, const std::string& search,
                          const std::string& replace) {
    size_t pos = 0;
    while((pos = subject.find(search, pos)) != std::string::npos) {
         subject.replace(pos, search.length(), replace);
         pos += replace.length();
    }
    return subject;
}

パフォーマンスが必要な場合は、入力文字列を変更する最適化された関数を次に示します。文字列のコピーは作成されません。

void ReplaceStringInPlace(std::string& subject, const std::string& search,
                          const std::string& replace) {
    size_t pos = 0;
    while((pos = subject.find(search, pos)) != std::string::npos) {
         subject.replace(pos, search.length(), replace);
         pos += replace.length();
    }
}

テスト:

std::string input = "abc abc def";
std::cout << "Input string: " << input << std::endl;

std::cout << "ReplaceString() return value: " 
          << ReplaceString(input, "bc", "!!") << std::endl;
std::cout << "ReplaceString() input string not changed: " 
          << input << std::endl;

ReplaceStringInPlace(input, "bc", "??");
std::cout << "ReplaceStringInPlace() input string modified: " 
          << input << std::endl;

出力:

Input string: abc abc def
ReplaceString() return value: a!! a!! def
ReplaceString() input string not modified: abc abc def
ReplaceStringInPlace() input string modified: a?? a?? def

6

テンプレート化されたインラインインプレース検索と置換:

template<class T>
int inline findAndReplace(T& source, const T& find, const T& replace)
{
    int num=0;
    typename T::size_t fLen = find.size();
    typename T::size_t rLen = replace.size();
    for (T::size_t pos=0; (pos=source.find(find, pos))!=T::npos; pos+=rLen)
    {
        num++;
        source.replace(pos, fLen, replace);
    }
    return num;
}

置換されたアイテムの数のカウントを返します(これを続けて実行する場合などに使用します)。それを使用するには:

std::string str = "one two three";
int n = findAndReplace(str, "one", "1");

4
私はGCCでこのサンプルを試しましたが、コンパイルできませんでした。T:: size_tの使用は好きではありませんでした。T :: size_tをタイプ名T :: size_typeに置き換えると、問題が解決します。
Andrew Wyatt

3

最も簡単な方法(書いたものに近いものを提供する)は、Boost.Regex、具体的にはregex_replaceを使用することです。

std :: stringには、find()メソッドとreplace()メソッドが組み込まれていますが、インデックスと文字列長を処理する必要があるため、操作が面倒です。


3
replace_allなどのブースト文字列アルゴリズムもあります(このような単純な置換には正規表現が少し重いかもしれません)。
UncleBens

3

これでうまくいくと思います。パラメータとしてconst char *を使用します。

//params find and replace cannot be NULL
void FindAndReplace( std::string& source, const char* find, const char* replace )
{
   //ASSERT(find != NULL);
   //ASSERT(replace != NULL);
   size_t findLen = strlen(find);
   size_t replaceLen = strlen(replace);
   size_t pos = 0;

   //search for the next occurrence of find within source
   while ((pos = source.find(find, pos)) != std::string::npos)
   {
      //replace the found string with the replacement
      source.replace( pos, findLen, replace );

      //the next line keeps you from searching your replace string, 
      //so your could replace "hello" with "hello world" 
      //and not have it blow chunks.
      pos += replaceLen; 
   }
}

size_type文字列がunsignedであることを考えると>=、ループ条件のチェックは常にになりますtruestd::string::nposそこで使用する必要があります。
Pavel Minaev

size_typeは署名されていません。多くのプラットフォームでは署名されていませんが、すべてではありません。
アラン

12
なぜこれがstd :: stringの一部ではないのですか?「検索して置換」操作を提供しない、プログラミングの世界の他の深刻なStringクラスはありますか?確かにそれは2つのイテレータを持っていて、それらの間のテキストを置き換えたいよりも一般的ですか?std :: stringは、調整可能なスペクトラムウインドシールドを備えた車のように感じることがありますが、ドライバーのウィンドウを押し下げる方法はありません。
Spike0xff 2009年

@ Spike0xffブーストroll_down_window
ta.speot.is 2012

1
@gustafr:私の間違い。古いコンパイラがsize_tを不適切に定義したシステムで作業しました。
アラン

1
// Replace all occurrences of searchStr in str with replacer
// Each match is replaced only once to prevent an infinite loop
// The algorithm iterates once over the input and only concatenates 
// to the output, so it should be reasonably efficient
std::string replace(const std::string& str, const std::string& searchStr, 
    const std::string& replacer)
{
    // Prevent an infinite loop if the input is empty
    if (searchStr == "") {
        return str;
    }

    std::string result = "";
    size_t pos = 0;
    size_t pos2 = str.find(searchStr, pos);

    while (pos2 != std::string::npos) {
        result += str.substr(pos, pos2-pos) + replacer;
        pos = pos2 + searchStr.length();
        pos2 = str.find(searchStr, pos);
    }

    result += str.substr(pos, str.length()-pos);
    return result;
}

1
最後の一致から新しい一致を検索するだけでよいので、アルゴリズムはposの最後の一致を注意深く追跡します。pos2は常に次の一致を保存するため、posとpos2の間の文字列を結果に連結し、posとpos2を進めます。他に一致するものが見つからない場合は、残りの文字列を連結して結果を生成します。
ビョルンガンスター2017

1
#include <string>

using std::string;

void myReplace(string& str,
               const string& oldStr,
               const string& newStr) {
  if (oldStr.empty()) {
    return;
  }

  for (size_t pos = 0; (pos = str.find(oldStr, pos)) != string::npos;) {
    str.replace(pos, oldStr.length(), newStr);
    pos += newStr.length();
  }
}

oldStrが空かどうかのチェックは重要です。何らかの理由でそのパラメーターが空の場合、無限ループに陥ります。

しかし、可能であれば、実証済みのテスト済みC ++ 11またはBoostソリューションを使用してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.