文字列の一部を別の文字列で置き換える


186

C ++で文字列の一部を別の文字列に置き換えることはできますか?

基本的に、私はこれをしたいと思います:

QString string("hello $name");
string.replace("$name", "Somename");

しかし、標準C ++ライブラリを使用したいと思います。


1
Cで文字列を置き換える関数何ですか?-すみません、C ++ではなくCです。投票を取り消したいです。
polygenelubricants

1
@poly私はこれだったな、すまんすまんがあまりにもC ++のために求められて思うだろうが、私はそれを見つけることができません
マイケルMrozek

1
質問にはstdタグがありますが、ブーストの文字列アルゴリズムに興味があるかもしれません。これには、置換アルゴリズムの幅広い選択肢が含まれます(インプレース/コピー、大文字と小文字を区別/大文字と小文字を区別せず、最初/最後/すべて/ n番目を置換) )。
UncleBens 2010

@Michael Mrozekは、上の1つありますstackoverflow.com/questions/3418231/...が、それは新しいですし、ここにあなたのでReplaceAll方法がより堅牢です。
dave-holm

回答:


288

文字列内の部分文字列を検索する関数(find)と、文字列の特定の範囲を別の文字列(replace)に置き換える関数があります。これらを組み合わせて、目的の効果を得ることができます。

bool replace(std::string& str, const std::string& from, const std::string& to) {
    size_t start_pos = str.find(from);
    if(start_pos == std::string::npos)
        return false;
    str.replace(start_pos, from.length(), to);
    return true;
}

std::string string("hello $name");
replace(string, "$name", "Somename");

コメントに応じて、私replaceAllはおそらく次のようなものになると思います:

void replaceAll(std::string& str, const std::string& from, const std::string& to) {
    if(from.empty())
        return;
    size_t start_pos = 0;
    while((start_pos = str.find(from, start_pos)) != std::string::npos) {
        str.replace(start_pos, from.length(), to);
        start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
    }
}

2
元の文字列に "$ name"のインスタンスが複数あり、それらをすべて置き換えたい場合、どうすれば修正できますか。
トムリース

1
なぜそうではなくfrom、参照toごとに渡されconstますか?fromない場合、あなたの機能は何ですか?-1そのために私から。
sbi

10
あなたが代わりに攻撃の提言として、それを言葉で表現しました可能性があるが、固定@sbi -単に私がめったに使用することを考えていない、私には発生しなかったconstと私はこのようなユーティリティメソッドを書いた場合、私は知っていた場合、私はそれを呼ぶだろう交換は有効でした
Michael Mrozek 2010

10
@マイケル:よし、私は自分の反対票を賛成票に変えた。却下constとは、C ++の最高のツールの1つを無視することです。参照ごとの受け渡しはconst、関数パラメーターのデフォルトモードである必要があります。(FTR、なしではconst、テンポラリを非const参照にバインドできないため、文字列リテラルを関数に渡すこともできませんでした。そのため、関数は書き込まれたことを実行しませんでした。)
sbi

19
これは2018年の唯一の解決策ですか?そうであり、C ++委員会がこれを読んでいる場合は、整理してください。恥ずかしいです。split(string、string)とreplace(string、string)してください!
user997112 2018

96

C ++ 11ではstd::regex、次のように使用できます。

#include <regex>
...
std::string string("hello $name");
string = std::regex_replace(string, std::regex("\\$name"), "Somename");

エスケープ文字をエスケープするには、二重の円記号が必要です。


std::regex_replaceQtの文字列を受け入れないことは確かです。
BartoszKP 2015

1
あなたが正しいです。偶然にも、QStringはQRexExpを受け入れるreplaceメソッドを提供し、Qt独自のものを使用できるようにします。しかし、私は、現在の答えは交換することによって修正することができると思いますstringstring.toStdString()
トム・

1
または、質問をQtに関連させないため、に変更Stringするだけstd::stringです。そうすることを検討してください-私は後で喜んであなたの答えに投票します。
BartoszKP 2015

5
生の文字列はのR"(\$name)"代わりに書き込むことができます"\\$name"
Jarod42

2
std :: regexの構築時間を考慮せずに、これが検索/置換よりどれほど遅くなりますか?
jw_

18

std::stringしているreplace、あなたが探しているものという方法がありますか?

あなたは試すことができます:

s.replace(s.find("$name"), sizeof("$name") - 1, "Somename");

私はまだ試していません。find()とのドキュメントを読んでくださいreplace()


2
std :: string replaceメソッドは、私が望むように2つの文字列を取りません。
トムリース

2
これは私にはうまくいきません。sizeofはstring( "Somename")。size()-1で置き換える必要があります
TimZaman

@TimZaman:それは私を困惑させます、ドキュメントはCスタイルの文字列から初期化できることを明確に述べています
SC Madsen 2014

5
2番目の引数は( "Somename"の長さではなく) "$ name"の長さでなければなりませんか?
Daniel Kiss

10

新しい文字列を返すには、これを使用します。

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 modified: " 
          << 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

ReplaceStringInPlace()でのsubject.replaceの呼び出しは、文字列を実際に変更していますか?
ダミアン

私はソースを簡単に見て、それは移動セマンティクスを使用して古い文字列の前を所定の位置に移動しているように見えます。そのため、コピーされませんが、挿入された新しいピースは古い文字列と古い文字列の末尾にコピーされます古い文字列のサイズ変更されたバッファにコピーされます。文字列が下にあるバッファ全体が再割り当てされる可能性がありますが、彼の例のように1を1に置き換えると、「その場で」、またはコピーなしで発生すると思いますが、文字列を拡張すると、古い文字列の最初の部分だけはコピーされません。
モート

6

はい、できますが、最初の文字列の位置を文字列のfind()メンバーで見つけ、それをreplace()メンバーで置き換える必要があります。

string s("hello $name");
size_type pos = s.find( "$name" );
if ( pos != string::npos ) {
   s.replace( pos, 5, "somename" );   // 5 = length( $name )
}

標準ライブラリの使用を計画している場合は、このすべてを非常によくカバーするC ++標準ライブラリという本コピーを手に入れる必要が あります。


1
それはsize_tであり、size_typeではありません
revo

1
これはstd :: string :: size_typeであり、size_tや装飾されていないsize_typeではありません。
jmucchiello 2013年

5

私はこれを一般的に使用します:

std::string& replace(std::string& s, const std::string& from, const std::string& to)
{
    if(!from.empty())
        for(size_t pos = 0; (pos = s.find(from, pos)) != std::string::npos; pos += to.size())
            s.replace(pos, from.size(), to);
    return s;
}

何も見つからなくなるstd::string::find()まで、検索された文字列の他の出現箇所を見つけるために繰り返し呼び出しますstd::string::find()。マッチstd::string::find()位置を返すので、イテレータを無効にする問題はありません。


4

これはオプションのように聞こえます

string.replace(string.find("%s"), string("%s").size(), "Something");

これを関数でラップすることもできますが、この1行のソリューションは許容できるようです。問題は、これにより最初の発生のみが変更されることです。ループすることもできますが、同じトークン(%s)を使用してこの文字列に複数の変数を挿入することもできます。


1
スタイルに似ているが、異なる文字列が混乱している^^str.replace(str.find("%s"), string("%s").size(), "Something");
PaulWürtz2017

3

すべての文字列がstd :: stringである場合sizeof()、C ++文字列ではなくC文字列を対象としているため、文字のカットオフを使用すると奇妙な問題が発生します。修正は、の.size()クラスメソッドを使用することですstd::string

sHaystack.replace(sHaystack.find(sNeedle), sNeedle.size(), sReplace);

これはインラインでsHaystackを置き換えます-その上で=代入を行う必要はありません。

使用例:

std::string sHaystack = "This is %XXX% test.";
std::string sNeedle = "%XXX%";
std::string sReplace = "my special";
sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
std::cout << sHaystack << std::endl;

2
wstring myString = L"Hello $$ this is an example. By $$.";
wstring search = L"$$";
wstring replace = L"Tom";
for (int i = myString.find(search); i >= 0; i = myString.find(search))
    myString.replace(i, search.size(), replace);

2

すばやく実行したい場合は、2スキャンアプローチを使用できます。疑似コード:

  1. 最初の解析。一致する文字の数を見つけます。
  2. 文字列の長さを拡張します。
  3. 2番目の解析。置換する文字列の末尾から開始します。それ以外の場合は、最初の文字列から文字をコピーします。

これをインプレースアルゴに最適化できるかどうかはわかりません。

C ++ 11のコード例ですが、1文字しか検索しません。

#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

void ReplaceString(string& subject, char search, const string& replace)
{   
    size_t initSize = subject.size();
    int count = 0;
    for (auto c : subject) { 
        if (c == search) ++count;
    }

    size_t idx = subject.size()-1 + count * replace.size()-1;
    subject.resize(idx + 1, '\0');

    string reverseReplace{ replace };
    reverse(reverseReplace.begin(), reverseReplace.end());  

    char *end_ptr = &subject[initSize - 1];
    while (end_ptr >= &subject[0])
    {
        if (*end_ptr == search) {
            for (auto c : reverseReplace) {
                subject[idx - 1] = c;
                --idx;              
            }           
        }
        else {
            subject[idx - 1] = *end_ptr;
            --idx;
        }
        --end_ptr;
    }
}

int main()
{
    string s{ "Mr John Smith" };
    ReplaceString(s, ' ', "%20");
    cout << s << "\n";

}

1
std::string replace(std::string base, const std::string from, const std::string to) {
    std::string SecureCopy = base;

    for (size_t start_pos = SecureCopy.find(from); start_pos != std::string::npos; start_pos = SecureCopy.find(from,start_pos))
    {
        SecureCopy.replace(start_pos, from.length(), to);
    }

    return SecureCopy;
}

2
このコードを(あなたの答えで)説明できますか?あなたはそのようにしてより多くの賛成を得られるかもしれません!
帽子の男14

1

私の実装では、文字列のサイズを1回だけ変更する必要があることを考慮して、置換を行うことができます。

template <typename T>
std::basic_string<T> replaceAll(const std::basic_string<T>& s, const T* from, const T* to)
{
    auto length = std::char_traits<T>::length;
    size_t toLen = length(to), fromLen = length(from), delta = toLen - fromLen;
    bool pass = false;
    std::string ns = s;

    size_t newLen = ns.length();

    for (bool estimate : { true, false })
    {
        size_t pos = 0;

        for (; (pos = ns.find(from, pos)) != std::string::npos; pos++)
        {
            if (estimate)
            {
                newLen += delta;
                pos += fromLen;
            }
            else
            {
                ns.replace(pos, fromLen, to);
                pos += delta;
            }
        }

        if (estimate)
            ns.resize(newLen);
    }

    return ns;
}

使用法は、たとえば次のようになります。

std::string dirSuite = replaceAll(replaceAll(relPath.parent_path().u8string(), "\\", "/"), ":", "");

0

私は今C ++を学習していますが、以前に投稿されたコードの一部を編集しているので、おそらくこのようなものを使用します。これにより、1つまたは複数のインスタンスを柔軟に置き換えることができ、開始点を指定することもできます。

using namespace std;

// returns number of replacements made in string
long strReplace(string& str, const string& from, const string& to, size_t start = 0, long count = -1) {
    if (from.empty()) return 0;

    size_t startpos = str.find(from, start);
    long replaceCount = 0;

    while (startpos != string::npos){
        str.replace(startpos, from.length(), to);
        startpos += to.length();
        replaceCount++;

        if (count > 0 && replaceCount >= count) break;
        startpos = str.find(from, startpos);
    }

    return replaceCount;
}

0

これは使用するのがさらに良いかもしれません

void replace(string& input, const string& from, const string& to)
{
    while(true)
    {
        size_t startPosition = input.find(from);
        if(startPosition == string::npos)
            break;
        input.replace(startPosition, from.length(), to);
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.