如果我们想把字符串里的某一段内容替换成新的文字,比如把句子中的 "Python" 改成 "C++",在 C++ 中可以使用 std::string 提供的 replace() 成员函数。
在实际开发中,我们既可以直接按已知的下标位置替换,也可以先查找目标位置再执行替换。
按位置替换:replace()
调用 replace() 最基础的方式,是指定起始下标和要替换的字符个数,把原字符串中对应位置的旧字符替换成新字符串。
语法:
str.replace(pos, count, newStr);说明:
调用 replace() 会直接修改原有的字符串变量 str,它的 3 个参数含义如下:
- pos(必选):起始下标位置,表示从哪个位置开始替换,下标从 0 开始计算。
- count(必选):要被替换掉的原字符个数。需要注意,这里是 “字符数量”,而不是结束下标。
- newStr(必选):用来放入该位置的新字符串内容。
示例 1:替换指定位置的子串
#include <iostream>
#include <string>
int main() {
std::string text = "Hello, Python!";
text.replace(7, 6, "C++");
std::cout << text << '\n';
return 0;
}运行结果如下。
Hello, C++!分析:
在 "Hello, Python!" 中,字母 'P' 位于下标 7,"Python" 一共有 6 个字符。因此 text.replace(7, 6, "C++") 表示从下标 7 开始删掉 6 个旧字符,再把 "C++" 放进去。
这里要注意,replace() 跟 substr() 有一个明显区别:substr() 不会改动原字符串,而是返回一个新字符串。replace() 则会直接修改原字符串本身。
此外,使用 replace(pos, count, newStr) 时,第 2 个参数 count 表示 “要替换掉多少个旧字符”,小伙伴们不要把它当成 “结束下标”。比如想把 "abc123xyz" 中间的 "123" 替换成 "ABC":
std::string text = "abc123xyz";
// 正确方式
text.replace(3, 3, "ABC");
// 错误方式
text.replace(3, 5, "ABC");在 "abc123xyz" 中,字符 '1' 的下标是 3,"123" 共有 3 个字符,所以第 2 个参数应该传 3,替换后得到 "abcABCxyz"。
如果误把字符 '3' 的下标 5 当作第 2 个参数传进去,程序就会从下标 3 开始连续删掉 5 个字符,把后面的 "xy" 也误删了,结果就会变成 "abcABCz"。
同时,替换操作并不要求新旧字符串的长度一样。新字符串可以比原来更长,也可以比原来更短,替换后原字符串的长度会自动跟着变化。下面我们来看一个例子。
示例 2:替换不同长度的内容
#include <iostream>
#include <string>
int main() {
std::string text = "I like C";
std::cout << "替换前长度:" << text.size() << '\n';
text.replace(7, 1, "Modern C++");
std::cout << "替换后内容:" << text << '\n';
std::cout << "替换后长度:" << text.size() << '\n';
return 0;
}运行结果如下。
替换前长度:8
替换后内容:I like Modern C++
替换后长度:17分析:
在这个例子中,原来的 "C" 只有 1 个字符,而换上去的 "Modern C++" 有 10 个字符。执行替换后,通过 size() 函数可以看到,字符串的总长度自动从 8 变成了 17。
结合 find() 查找并替换
前面的例子都是我们已经提前数好了目标在哪个下标位置。但在实际开发中,我们一般不会去人工数下标,而是先调用 find() 函数找到目标字符串的位置,再把找到的下标传给 replace() 完成替换。
示例 3:先查找再替换
#include <iostream>
#include <string>
int main() {
std::string text = "Learn Python with lvyenet";
std::string target = "Python";
std::size_t pos = text.find(target);
if (pos != std::string::npos) {
text.replace(pos, target.size(), "C++");
}
std::cout << text << '\n';
return 0;
}运行结果如下。
Learn C++ with lvyenet分析:
在上面例子中,text.find(target) 先找出 "Python" 首次出现的起始下标 6 并存入 pos。确认找到后,再调用 text.replace(pos, target.size(), "C++") 把原单词替换成 "C++"。
这种写法比手动写死数字 6 灵活得多。以后就算把要替换的目标词 target 改成其他单词,查找位置和替换长度都会由程序自动计算,我们不需要再去手动修改 replace() 里的参数。
注意 结合 find() 和 replace() 时,必须先判断 pos != std::string::npos。如果没找到目标就直接传入 npos,会触发越界错误导致程序崩溃。
循环替换所有匹配项
如果一段文本里出现了多个相同的目标词,调用一次 find() 配合 replace() 只会替换第一处找到的目标。那如果想要把它们全部替换掉,应该怎么做呢?
其实思路很简单:我们只需要结合 while 循环,反复执行 “查找 → 替换”,并在每次替换后把下一次查找的起始位置往后推进即可。请看下面例子。
示例 4:替换所有匹配项
#include <iostream>
#include <string>
int main() {
std::string text = "py-go-py";
std::string target = "py";
std::string replacement = "cpp";
std::size_t pos = text.find(target);
while (pos != std::string::npos) {
text.replace(pos, target.size(), replacement);
pos = text.find(target, pos + replacement.size());
}
std::cout << text << '\n';
return 0;
}运行结果如下。
cpp-go-cpp分析:
进入循环前,text.find(target) 先找到第 1 个 "py" 的下标 pos 为 0。接着 while 循环的执行过程如下:
- 第 1 次循环:把下标 0 处的
"py"换成"cpp",字符串变为"cpp-go-py"。接着从下标0 + 3(即 3)继续往后找,得到第 2 个"py"的位置pos为 7。 - 第 2 次循环:把下标 7 处的
"py"换成"cpp",字符串变为"cpp-go-cpp"。接着从下标7 + 3(即 10)往后找,返回std::string::npos,循环结束。
这里小伙伴们要特别留意:循环内部每次更新查找起点时,写的是 pos + replacement.size(),也就是直接跳过刚刚替换进去的新词。
为什么要跳过新词的长度呢?假如我们要把句子里的 "C" 替换成 "C++",换上去的新词首字符还是 'C'。如果下次查找不跳过整个 "C++",程序又会在刚换好的新词里找到 'C' 并继续替换,最终陷入死循环。
删除与清空字符
如果我们的目的不是替换成别的词,而是想把字符串里的某一段字符直接删掉,或者清空整个字符串,可以直接调用 std::string 提供的 erase() 和 clear() 函数:
erase(pos, count):从下标 pos 开始直接删除 count 个字符,比如 text.erase(0, 3) 表示删除开头的 3 个字符。如果省略 count,则从下标 pos 一直删除到末尾。
clear():直接把整个字符串里的所有字符全部清空,使其变成空字符串 "",比如 text.clear()。
