ある文字のすべての出現箇所をstd::string
内の別の文字に置き換えるための効果的な方法は何ですか?
std::string
にはそのような関数は含まれていませんが、replace
ヘッダーからスタンドアロンのalgorithm
関数を使用することができます。
#include <algorithm>
#include <string>
void some_func() {
std::string s = "example string";
std::replace( s.begin(), s.end(), 'x', 'y'); // replace all 'x' to 'y'
}
私は ブーストソリューション にも投げたいと思った。
#include <boost/algorithm/string/replace.hpp>
// in place
std::string in_place = "blah#blah";
boost::replace_all(in_place, "#", "@");
// copy
const std::string input = "blah#blah";
std::string output = boost::replace_all_copy(input, "#", "@");
質問はcharacter
置換に集中していますが、このページが非常に便利だとわかったので(特に Konrad のコメント)、このより一般化された実装を共有したいと思います。 substrings
も処理します:
std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) {
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(); // Handles case where 'to' is a substring of 'from'
}
return str;
}
使用法:
std::cout << ReplaceAll(string("Number Of Beans"), std::string(" "), std::string("_")) << std::endl;
std::cout << ReplaceAll(string("ghghjghugtghty"), std::string("gh"), std::string("X")) << std::endl;
std::cout << ReplaceAll(string("ghghjghugtghty"), std::string("gh"), std::string("h")) << std::endl;
出力:
Number_Of_Beans
XXjXugtXty
hhjhugthty
編集:
パフォーマンスが懸念される場合は、何も返さず(void
)、文字列str
に対して直接変更を実行することにより、上記の方法をより適切な方法で実装できます引数として、by valueの代わりにbyby addressを渡しました。これにより、結果を返す間、元の文字列の無駄で高価なコピーが回避されます。それでは...
コード:
static inline void ReplaceAll2(std::string &str, const std::string& from, const std::string& to)
{
// Same inner code...
// No return statement
}
これが他の人にとって役立つことを願っています...
トランスポートプロトコルでは\ 0バイトが許されないため、すべての0x00バイトが "\ 1\x30"に、すべての0x01バイトが "\ 1\x31"に置き換えられる大きなバイナリBLOBを想像してください。
以下の場合
提供されたソリューションは適用できません(1文字のみを置き換えるため)、またはパフォーマンス上の問題があります。なぜなら、string :: replaceを何度も呼び出すため、BLOBのサイズのコピーが何度も生成されるためです。 (私はブーストソリューションを知りません、多分それはその観点から大丈夫です)
これは、ソース文字列内のすべての出現箇所に沿って進み、新しい文字列をpiece once ずつ構築します。
void replaceAll(std::string& source, const std::string& from, const std::string& to)
{
std::string newString;
newString.reserve(source.length()); // avoids a few memory allocations
std::string::size_type lastPos = 0;
std::string::size_type findPos;
while(std::string::npos != (findPos = source.find(from, lastPos)))
{
newString.append(source, lastPos, findPos - lastPos);
newString += to;
lastPos = findPos + from.length();
}
// Care for the rest after last occurrence
newString += source.substr(lastPos);
source.swap(newString);
}
単一の文字を単純に検索して置き換えると、次のようになります。
s.replace(s.find("x"), 1, "y")
文字列全体に対してこれを行うには、s.find
がnpos
を返し始めるまでループするのが簡単です。ループを終了するためにrange_error
をキャッチすることもできると思いますが、それはちょっと醜いです。
複数の文字を置き換えることを検討していて、std::string
のみを扱う場合は、このスニペットは機能します。sHaystackのsNeleをsReplaceに置き換えます。sNeedleとsReplaceは同じサイズである必要はありません。このルーチンは、左から右に最初に見つかったものだけではなく、whileループを使用してすべてのオカレンスを置き換えます。
while(sHaystack.find(sNeedle) != std::string::npos) {
sHaystack.replace(sHaystack.find(sNeedle),sNeedle.size(),sReplace);
}
Kirillが示唆したように、replaceメソッドを使うか、文字列に沿って各charを個別に置き換えながら繰り返します。
代わりに、あなたがする必要があるものに応じてfind
メソッドまたはfind_first_of
を使うことができます。これらの解決策のどれも一度に仕事をしないでしょう、しかしあなたはそれらをあなたのために働くようにするべきであるいくつかの余分なコード行であなたはすべきです。 :-)
#include <iostream>
#include <string>
using namespace std;
// Replace function..
string replace(string Word, string target, string replacement){
int len, loop=0;
string nword="", let;
len=Word.length();
len--;
while(loop<=len){
let=Word.substr(loop, 1);
if(let==target){
nword=nword+replacement;
}else{
nword=nword+let;
}
loop++;
}
return nword;
}
//Main..
int main() {
string Word;
cout<<"Enter Word: ";
cin>>Word;
cout<<replace(Word, "x", "y")<<endl;
return 0;
}
単純な状況では、これはstd :: string(既に使用されている)以外のライブラリを使用しなくても非常にうまく機能します。
some_string で、文字 a のすべての出現箇所を文字 b に置き換えます。
for (size_t i = 0; i < some_string.size(); ++i) {
if (some_string[i] == 'a') {
some_string.replace(i, 1, "b");
}
}
文字列が大きい場合、または複数の呼び出しを置き換えることが問題になる場合は、この回答に記載されている手法を適用できます。 https://stackoverflow.com/a/29752943/3622300
std::string
sを使用したい場合は、このサンプルアプリのstrsub
関数をそのまま使用することも、ほぼ同じ目的を達成するために別の種類または一連のパラメータを使用する場合は更新することもできます。基本的には、std::string
のプロパティと機能を使用して、一致する一連の文字をすばやく消去し、目的の文字をstd::string
内に直接挿入します。この置換操作を行うたびに、一致する文字を置換する文字列がまだ見つかった場合はオフセットが更新され、それ以上置換できない場合は最後の更新からの状態の文字列が返されます。
#include <iostream>
#include <string>
std::string strsub(std::string stringToModify,
std::string charsToReplace,
std::string replacementChars);
int main()
{
std::string silly_typos = "annoiiyyyng syyyllii tiipos.";
std::cout << "Look at these " << silly_typos << std::endl;
silly_typos = strsub(silly_typos, "yyy", "i");
std::cout << "After a little elbow-grease, a few less " << silly_typos << std::endl;
silly_typos = strsub(silly_typos, "ii", "y");
std::cout << "There, no more " << silly_typos << std::endl;
return 0;
}
std::string strsub(std::string stringToModify,
std::string charsToReplace,
std::string replacementChars)
{
std::string this_string = stringToModify;
std::size_t this_occurrence = this_string.find(charsToReplace);
while (this_occurrence != std::string::npos)
{
this_string.erase(this_occurrence, charsToReplace.size());
this_string.insert(this_occurrence, replacementChars);
this_occurrence = this_string.find(charsToReplace,
this_occurrence + replacementChars.size());
}
return this_string;
}
あなたがパラメータとしてstd::string
sを使うことに頼らず、代わりにCスタイルの文字列を渡すことができるのであれば、以下の更新されたサンプルを見ることができます:
#include <iostream>
#include <string>
std::string strsub(const char * stringToModify,
const char * charsToReplace,
const char * replacementChars,
uint64_t sizeOfCharsToReplace,
uint64_t sizeOfReplacementChars);
int main()
{
std::string silly_typos = "annoiiyyyng syyyllii tiipos.";
std::cout << "Look at these " << silly_typos << std::endl;
silly_typos = strsub(silly_typos.c_str(), "yyy", "i", 3, 1);
std::cout << "After a little elbow-grease, a few less " << silly_typos << std::endl;
silly_typos = strsub(silly_typos.c_str(), "ii", "y", 2, 1);
std::cout << "There, no more " << silly_typos << std::endl;
return 0;
}
std::string strsub(const char * stringToModify,
const char * charsToReplace,
const char * replacementChars,
uint64_t sizeOfCharsToReplace,
uint64_t sizeOfReplacementChars)
{
std::string this_string = stringToModify;
std::size_t this_occurrence = this_string.find(charsToReplace);
while (this_occurrence != std::string::npos)
{
this_string.erase(this_occurrence, sizeOfCharsToReplace);
this_string.insert(this_occurrence, replacementChars);
this_occurrence = this_string.find(charsToReplace,
this_occurrence + sizeOfReplacementChars);
}
return this_string;
}
古い学校 :-)
std::string str = "H:/recursos/audio/youtube/libre/falta/";
for (int i = 0; i < str.size(); i++) {
if (str[i] == '/') {
str[i] = '\\';
}
}
std::cout << str;
結果:
H:\ recursos\audio\youtube\libre\falta \
これはうまくいきます!私はこのようなものを本屋アプリに使いました。そこでは在庫がCSVに保存されていました(.datファイルのように)。しかし、単一の文字の場合は、置き換え文字が単一の文字、たとえば '|'であることを意味し、二重引用符で囲む必要があります。無効な変換const charをスローしないようにするため。
#include <iostream>
#include <string>
using namespace std;
int main()
{
int count = 0; // for the number of occurences.
// final hold variable of corrected Word up to the npos=j
string holdWord = "";
// a temp var in order to replace 0 to new npos
string holdTemp = "";
// a csv for a an entry in a book store
string holdLetter = "Big Java 7th Ed,Horstman,978-1118431115,99.85";
// j = npos
for (int j = 0; j < holdLetter.length(); j++) {
if (holdLetter[j] == ',') {
if ( count == 0 )
{
holdWord = holdLetter.replace(j, 1, " | ");
}
else {
string holdTemp1 = holdLetter.replace(j, 1, " | ");
// since replacement is three positions in length,
// must replace new replacement's 0 to npos-3, with
// the 0 to npos - 3 of the old replacement
holdTemp = holdTemp1.replace(0, j-3, holdWord, 0, j-3);
holdWord = "";
holdWord = holdTemp;
}
holdTemp = "";
count++;
}
}
cout << holdWord << endl;
return 0;
}
// result:
Big Java 7th Ed | Horstman | 978-1118431115 | 99.85
私は現在CentOSを使っていますが、私のコンパイラのバージョンは以下です。 C++バージョン(g ++)、C++ 98デフォルト:
g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.