CC++ & Algorithm

string的修改操作:插入、删除与替换

困难2
语言版本:通用
概述:学习如何在字符串的任意位置插入新内容、删除一部分字符、或者把某些字符替换成其他内容,就像用橡皮擦和超级胶水修改作文。

字符串的“修改手术”:插入、删除与替换

你有没有写过作文之后发现少了一个字,需要“插入”到句子中间?或者写错了几个字,想把它们“删除”掉?又或者想全文把“小明”改成“小红”——这就是“替换”。
在编程里,我们经常要对字符串做同样的操作。C++ 的 string 提供了 inserterasereplace 这些“手术工具”,能直接修改原字符串。Python 的字符串虽然不能变(像一张写好的纸,不能擦改),但我们可以用“剪接”的方法——切片加拼接——来生成新字符串,达到同样的效果。


从生活中的例子引入(保留原有内容)

假设你在作业本上写了一段话,写到一半发现少了一个字,需要“插入”到某个位置;或者写错了几个字,要“删除”它们;又或者想把全文中的“小明”都改成“小红”,这就是“替换”。

在编程中,字符串也经常需要这样修改。C++ 的 string 提供了 inserterasereplace 成员函数,Python 字符串虽然不可变,但可以通过切片和拼接实现类似效果。


STL 原理和使用方法

insert 插入:在任意位置“加塞”

insert 就像在排队时插队——你选择一个位置,把新内容塞进去,后面的人(字符)自动往后挪。

基本用法(保留原有内容):

  • insert(pos, str):在位置 pos 之前插入字符串 strpos 可以是下标或迭代器。
  • 还可以指定插入次数或插入子串的一部分(重载版本)。

更多重载版本(新增补充):

  • insert(pos, n, ch):在位置 pos 前插入 n 个相同字符 ch
  • insert(pos, str, subpos, sublen):插入 str 中从 subpos 开始的 sublen 个字符。
  • insert(iterator, ch):在迭代器指向的位置前插入一个字符。
  • insert(iterator, n, ch):在迭代器位置前插入 n 个字符。
  • insert(iterator, first, last):插入另一个容器(如 stringvector)中迭代器区间 [first, last) 的字符。

生活例子
你想给朋友发一条消息:“我昨天去了公园”,但突然想起昨天还去了超市,想改成“我昨天去了公园和超市”。你可以在“公园”后面插入“和超市”。在代码里就是 s.insert(6, "和超市")(假设下标6是“公”字前面的位置)。

Python 实现技巧(新增):

  • 因为 Python 字符串不可变,所以用切片拼接:
    s = "我昨天去了公园"
    pos = s.find("公园")  # 找到"公园"起始位置,假设为5
    s = s[:pos] + "和超市" + s[pos:]  # 在"公园"前插入
    
  • 如果想插入多个相同字符,可以用 "*" * n 生成字符串再拼接。

erase 删除:任意范围“擦除”

erase 就像用橡皮擦掉一段文字,后面的字符会自动往前补齐。

基本用法(保留原有内容):

  • erase(pos, len):从 pos 开始删除 len 个字符。如果省略 len,则删除从 pos 到末尾。
  • erase(iterator):删除迭代器指向的单个字符。
  • erase(first, last):删除迭代器区间的字符。

更多重载版本(新增):

  • C++11 之后,erase 返回一个迭代器,指向被删除元素的下一个位置(方便连续删除)。

生活例子
你写了一行字“我今天吃了苹果和香蕉”,但发现“苹果和”三个字是多余的,想删掉。假设“苹果”在位置6(从0开始),长度为3,只需要 s.erase(6, 3) 即可。

常见误区(新增):

  • C++ 中调用 erase(pos, len) 时,如果 pos 越界(大于等于字符串长度),会导致程序崩溃(未定义行为)。
  • 如果 len 很大(超过从 pos 到末尾的长度),不会报错,只会删到末尾。
  • 使用迭代器删除后,原来的迭代器会失效(不能再使用),必须重新获取。

Python 实现(新增):

  • 删除固定位置:s = s[:pos] + s[pos+len:]
  • 删除单个字符:s = s[:i] + s[i+1:]

replace 替换:精确“改写”

replace 就像用修正带覆盖一段文字,再写上新的内容。

基本用法(保留原有内容):

  • replace(pos, len, str):将从 pos 开始的 len 个字符替换为 str
  • 也可以用迭代器指定范围进行替换。

更多重载版本(新增):

  • replace(pos, len, str, subpos, sublen):用 str 的子串来替换。
  • replace(iterator_first, iterator_last, str):用迭代器区间指定范围。
  • replace(pos, len, n, ch):将区间替换为 n 个字符 ch

生活例子
你的作文里把“小明”写成了“小红”,现在要改回来。先用 find 找到“小红”的位置,然后替换成“小明”。或者更简单:如果知道位置,直接替换。

C++ 与 Python 差异(新增):

  • C++ 的 replace 只替换一次(你指定的位置和长度),要全部替换需要自己写循环或用算法 std::replace(但那是替换字符,不是子串)。
  • Python 的 replace 默认替换所有匹配的子串,也可以指定替换次数:s.replace("old", "new", count)

常见陷阱(新增):

  • 替换的字符串长度可以和原区间不同,字符串会自动调整长度。
  • 如果替换后字符串变长,后面的字符会往后移;变短则往前移。

新手容易犯的错误(新增详细列表)

  1. 下标越界
    • C++:pos 必须小于等于 size()(对于 insert 可以等于 size,表示末尾插入),否则程序崩溃。
    • Python:切片不会报错,但拼接时如果下标写错,可能得到错误结果。
  2. 迭代器失效
    • 在 C++ 中,插入或删除字符后,之前保存的迭代器(例如 it = s.begin())会失效,不能再使用。需要重新获取。
  3. 认为 Python 字符串可以修改
    • 新手容易写 s.replace(...) 之后以为 s 变了,其实要重新赋值:s = s.replace(...)
  4. C++ 中 replace 只替换一次
    • 如果想替换所有“abc”,需要自己写循环,不能直接 s.replace(...) 完成全部替换。
  5. erase 省略 len 时的行为
    • 如果调用 s.erase(pos) 而没有给出 len,会从 pos 删除到末尾,不是只删一个字符。要删一个字符用 s.erase(pos, 1) 或迭代器版。

完整示例(保留原有代码并增加一个综合案例)

原有 C++ 代码(带详细注释)

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

int main() {
    string s = "I love coding";

    // 1. insert:在 "love" 前面插入 "really "
    s.insert(2, "really ");  // 下标2是'l'的位置,插入后变成 "I really love coding"
    cout << "insert后: " << s << endl;

    // 2. erase:删除从下标0开始的2个字符("I "),剩下 "really love coding"
    s.erase(0, 2);
    cout << "erase后: " << s << endl;

    // 删除最后一个字符 'g'
    s.erase(s.size() - 1);
    cout << "删除最后一个字符: " << s << endl;

    // 3. replace:将 "coding" 替换为 "programming"
    // 先用 find 找到 "coding" 的位置
    size_t pos = s.find("coding");
    if (pos != string::npos) {
        s.replace(pos, 6, "programming");  // "coding" 长度为6
        cout << "replace后: " << s << endl;
    }

    // 使用迭代器版本插入(在开头插入一个字符)
    s.insert(s.begin(), 'A');
    cout << "开头插入 'A': " << s << endl;

    // erase 第二个字符(现在A在位置0,第二个字符是空格?我们删掉那个空格)
    s.erase(s.begin() + 1);   // 删除下标1的字符
    cout << "删除第二个字符: " << s << endl;

    return 0;
}

原有 Python 代码(带详细注释)

# Python字符串不可变,每次修改都会创建新字符串
s = "I love coding"

# 1. insert:在 "love" 前面插入 "really "
# 先找到 "love" 的位置
pos = s.find("love")
if pos != -1:
    s = s[:pos] + "really " + s[pos:]
print(f"insert后: {s}")

# 2. erase:删除从下标0开始的2个字符
s = s[2:]  # 切片,跳过前两个字符
print(f"erase后: {s}")

# 删除最后一个字符
s = s[:-1]
print(f"删除最后一个字符: {s}")

# 3. replace:将 "coding" 替换为 "programming"
# Python 有直接的 replace 方法(返回新字符串)
s = s.replace("coding", "programming")
print(f"replace后: {s}")

# 若要在开头插入字符
s = 'A' + s
print(f"开头插入 'A': {s}")

# 删除第二个字符
if len(s) > 1:
    s = s[:1] + s[2:]  # 保留第一个字符,跳过第二个,再连上剩余
print(f"删除第二个字符: {s}")

新增综合示例:模拟修改朋友圈文案(C++ 和 Python 对照)

场景
你发了一条朋友圈:“今天和同桌去图书馆,然后去吃了火锅(超辣)”。
后来你发现:“同桌”改成“好朋友”更准确;删掉括号里的“超辣”;在“图书馆”后面插入“和网吧”?不,改成“然后去公园散步”。

C++ 实现

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

int main() {
    string s = "今天和同桌去图书馆,然后去吃了火锅(超辣)";
    
    // 1. 将"同桌"替换为"好朋友"
    size_t pos = s.find("同桌");
    if(pos != string::npos) {
        s.replace(pos, 6, "好朋友");   // "同桌" 两个字占用6字节(UTF-8,假设)
    }
    cout << "替换后: " << s << endl;   // 注意:中文字符在UTF-8中每个占3字节,这里简化
    
    // 2. 删除括号及里面的"超辣"(包括括号)
    pos = s.find("(超辣)");   // 全角括号
    if(pos != string::npos) {
        s.erase(pos, 9);       // 假设删除9字节
    }
    cout << "删除后: " << s << endl;
    
    // 3. 在"图书馆"后面插入",然后去公园散步"
    pos = s.find("图书馆");
    if(pos != string::npos) {
        s.insert(pos + 9, ",然后去公园散步");  // 找到"图书馆"结束位置
    }
    cout << "最终: " << s << endl;
    
    return 0;
}

Python 实现(需要处理编码,这里简化为普通字符串):

s = "今天和同桌去图书馆,然后去吃了火锅(超辣)"

# 1. 替换"同桌"为"好朋友"
s = s.replace("同桌", "好朋友")
print(f"替换后: {s}")

# 2. 删除"(超辣)"
s = s.replace("(超辣)", "")   # 直接替换为空字符
print(f"删除后: {s}")

# 3. 在"图书馆"后插入",然后去公园散步"
pos = s.find("图书馆")
if pos != -1:
    s = s[:pos + 3] + ",然后去公园散步" + s[pos + 3:]   # 每个汉字占1个位置(Python中按字符)
print(f"最终: {s}")

注意:中文字符在 C++ 中可能占用多个字节(如 UTF-8 下每个汉字 3 字节),使用下标时要小心。实际开发中建议使用 wstringstring 配合长度计算。Python 的字符串以 Unicode 字符为单位,处理中文更方便。


总结要点和注意事项(保留原有内容并补充)

  • C++ 的 inserterasereplace 会直接修改原字符串,效率较高。
  • Python 字符串不可变,每次修改都生成新字符串,大量修改时建议先转为 list 再操作,最后用 ''.join 转回来。
  • 注意下标范围,越界会导致未定义行为(C++)或错误(Python)。
  • replace 在 C++ 中只替换指定位置的一次,而 Python 的 replace 默认替换所有匹配项(可指定次数)。
  • 使用迭代器时注意迭代器失效问题(插入删除后后续迭代器可能无效)。

新增提醒

  • 在 C++ 中,如果要插入或删除多个字符,优先考虑重载版本(如 insert(pos, n, ch))而非循环操作,效率更高。
  • 对大量字符串修改(如拼接大量片段),C++ 可用 string::reserve 提前分配内存,Python 用 list + join

相关指引(新增)

学会插入、删除和替换之后,你可以继续探索这些知识:

  1. 字符串查找findrfindfind_first_of 等,能帮你准确找到要修改的位置。
  2. 子串提取substr(C++)/切片(Python)可以取出字符串的一部分。
  3. 字符串比较compare(C++)/比较运算符(Python)判断两个字符串是否相等。
  4. 字符串流处理:C++ 的 stringstream 可以把字符串当做输入输出流,方便格式化;Python 的 StringIO 类似。
  5. 正则表达式:当需要复杂模式匹配和替换时(比如把所有数字替换成*),正则表达式是最强工具(C++ <regex>,Python re 模块)。
  6. Python 的 strbytes:了解编码转换,避免中文乱码。

把字符串的修改操作练熟,你就掌握了处理文本的基本功。现在去试试修改你的“作文”吧!

例题精讲

1单选题

在C++中,已知string s = "HelloWorld"; 执行 s.insert(5, " "); 后,s的值是什么?

A"Hello World"
B"HelloWorld "
C"Hello World "
D"HelloWorld"
2单选题

执行以下代码后,字符串s的值是什么?\nstring s = "abcdefg";\ns.erase(2, 3);

A"abfg"
B"abefg"
C"abcfg"
D"abdeg"
3判断题

在C++中,string的replace函数只能替换指定长度的字符,不能插入或删除字符。

4填空题
完善代码,实现在字符串s的末尾插入一个感叹号。\nstring s = "Hello";\ns.____(s.____(), '!');
5填空题
补全代码,删除字符串s中从位置3开始的2个字符。\nstring s = "abcdef";\ns.____(3, ____);