用另一个字符串替换字符串的一部分

我如何使用标准c++库替换字符串的一部分与另一个字符串?

QString s("hello $name");  // Example using Qt.
s.replace("$name", "Somename");
421904 次浏览

使用std::string::replace:

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

是的,你可以这样做,但你必须用字符串的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++标准库的书,它很好地涵盖了所有这些东西。

有一个函数用于查找字符串中的子字符串(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'
}
}

要返回新字符串,请使用以下命令:

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

我通常用这个:

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()返回匹配的位置,所以我们不存在使迭代器失效的问题。

在c++ 11中,你可以像这样使用std::regex:

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

对于转义字符,需要使用双反斜杠。

string.replace(string.find("%s"), string("%s").size(), "Something");
你可以把它包装在一个函数中,但这一行解决方案听起来是可以接受的。 问题是这只会改变第一次出现,你可能想要遍历它,但它也允许你用相同的令牌(%s)在这个字符串中插入几个变量

我现在正在学习c++,但是编辑之前发布的一些代码,我可能会使用这样的东西。这为您提供了替换一个或多个实例的灵活性,还允许您指定起始点。

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

如果所有字符串都是std::string,如果使用sizeof(),你会发现字符截断的奇怪问题,因为它是针对C字符串,而不是c++字符串。修复方法是使用std::string.size()类方法。

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;

如果你想快速完成,你可以使用两次扫描的方法。 伪代码:< / p >

  1. 首先解析。找出有多少匹配字符。
  2. 展开字符串的长度。
  3. 第二个解析。从字符串的末尾开始,当我们得到一个匹配,我们替换,否则我们只是复制字符从第一个字符串。

我不确定这是否可以优化到一个到位的算法。

和一个c++ 11代码示例,但我只搜索一个字符。

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


}

我自己的实现,考虑到字符串只需要调整一次大小,然后替换就可以发生。

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

这个用起来可能更好

void replace(string& input, const string& from, const string& to)
{
auto pos = 0;
while(true)
{
size_t startPosition = input.find(from, pos);
if(startPosition == string::npos)
return;
input.replace(startPosition, from.length(), to);
pos += to.length();
}
}

你可以使用这个代码来删除减法,也可以替换,也可以删除额外的空白。 代码:< / p >

#include<bits/stdc++.h>
using namespace std;


void removeSpaces(string &str)
{
int n = str.length();
int i = 0, j = -1;


bool spaceFound = false;
while (++j <= n && str[j] == ' ');


while (j <= n)
{
if (str[j] != ' ')
{
          

if ((str[j] == '.' || str[j] == ',' ||
str[j] == '?') && i - 1 >= 0 &&
str[i - 1] == ' ')
str[i - 1] = str[j++];
else str[i++] = str[j++];
 

spaceFound = false;
}
else if (str[j++] == ' ')
{
if (!spaceFound)
{
str[i++] = ' ';
spaceFound = true;
}
}
}


if (i <= 1)
str.erase(str.begin() + i, str.end());
else str.erase(str.begin() + i - 1, str.end());
}
int main()
{
string s;
cin >> s;


for(int i = s.find("WUB"); i >= 0; i = s.find("WUB"))
s.replace(i,3," ");
removeSpaces(s);
cout << s << endl;


return 0;
}

那么加速方案呢:

boost::replace_all(value, "token1", "token2");