string的基本操作:构造、拼接与遍历
中等7字符串基础:构造、拼接与遍历——像搭积木火车一样简单
什么是字符串?为什么需要操作它?
字符串就是一连串字符排成的队伍,比如你的名字 "小明"、一句歌词 "Twinkle twinkle little star",甚至一个密码 "abc123"。在编程中,字符串是最常用的数据类型之一——你想存储聊天消息、处理用户输入、生成文件名,都离不开它。
C++ 的 std::string 和 Python 的 str 都提供了方便的操作来构造、拼接和遍历。这就好比你有了一盒字母积木,想怎么拼就怎么拼。
生活中的类比:积木火车
想象你有一盒字母积木:
- 构造:从空盒子开始,或者直接摆好一列固定的积木(比如 "Hello")。
- 拼接:把两列积木首尾相连,变成一列更长的火车。
- 遍历:从火车头走到火车尾,看看每个车厢里是什么字母。
编程里的字符串就是这列积木火车,每个字符是一个车厢。
1. 构造:创建字符串(搭好火车车厢)
为什么需要不同的构造方式?
有时候你刚开始手里什么都没有(空字符串),有时候已经知道火车的内容(比如 "Hello"),有时候想用同一个字母重复很多遍(比如 "AAAAA")。不同的构造方式让你更灵活地准备数据。
C++ 中的构造
- 默认构造:
string s1;——创建一个空箱子,里面没有字母。 - C风格字符串构造:
string s2("Hello");——把已有的字母序列放进箱子。 - 重复字符构造:
string s3(5, 'A');——一次放入 5 个字母 'A',得到 "AAAAA"。 - 拷贝构造:
string s4(s2);——复制一份一模一样的火车,s4 也有 "Hello"。
生活例子:老师让你准备班级点名册,你可以:
- 拿一个空白本子(默认构造)
- 用打印好的名单直接订上(C字符串构造)
- 全班都姓“王”,写 5 个“王”字(重复字符构造)
- 复印一份别的班的花名册(拷贝构造)
Python 中的构造
Python 更随意:
- 直接赋值:
s = "hello"(引号里写什么就是什么) - 用
str()转换:s = str(123)→"123"(把数字变成字符串) - 重复字符:
'A' * 5→"AAAAA"(乘号就是重复) - 空字符串:
s = ""(一对空引号)
注意:Python 字符串一旦创建就不能修改,所以赋值操作其实是换了一列新火车(后面的拼接会详细说)。
2. 拼接:连接字符串(把火车连起来)
为什么需要拼接?
你想把名字和祝福语连起来:"小明" + "生日快乐" → "小明生日快乐"。或者把多个单词拼成一个句子。
C++ 中的拼接
C++ 的 string 是可变的,可以直接用运算符或成员函数修改自身:
-
+运算符:string s5 = s2 + " " + s3;
这不会改变 s2 和 s3,而是造一列新火车。就像把两列积木火车分开搬,然后首尾接在一起。 -
+=运算符:s5 += "!";
直接在原有火车末尾加一节车厢,火车变长了,原来的 s5 被改写了。 -
append()成员函数:s6 = s2.append(" World");
效果和+=类似,更明确地表示“追加”。注意:append()会修改 s2 本身并返回引用,所以 s6 和 s2 指向同一列火车(不过这里 s6 是新变量接收了返回值,但 s2 也被改了)。
Python 中的拼接
Python 字符串不可变,所以每次 + 或 += 都创建新字符串,旧字符串被丢弃。
-
+运算符:s5 = s2 + " " + s3
把 s2、空格、s3 三列火车拼成一列,但 s2 和 s3 仍然原地不动。 -
+=运算符:s5 += "!"
看起来是追加,实际上是:先把 s5 的内容和 "!" 拼成一个新字符串,再把 s5 指向这个新字符串。原来的 s5 对象被销毁。 -
''.join()方法(推荐大量拼接):parts = ["Hello", "World"] s6 = " ".join(parts) # 用空格连接列表里的字符串,效率高这好比把一堆积木车厢按顺序串起来,而不是一个个地粘。
新手容易犯的错误:在 Python 里用循环
s = s + ch拼接大量字符,每次循环都创建新字符串,速度慢且浪费内存。正确的做法是先把字符放列表里,最后用join。
3. 遍历:逐个访问字符(检阅火车车厢)
为什么要遍历?
你要检查每个字母是不是大写、统计有多少个元音、或者打印每个字符……遍历让你能挨个处理车厢里的字母。
C++ 中的遍历
-
下标访问:
s[i]
就像火车车厢编号从 0 开始。注意i必须小于字符串长度(s.size()),否则越界,程序可能崩溃。 -
范围 for 循环(C++11起):
for (char ch : s)
最简洁的方式,自动从头到尾拿到每个字符。相当于你从车头走到车尾,每节车厢看一眼,但不知道车厢编号。 -
使用迭代器:
for (auto it = s.begin(); it != s.end(); ++it)
迭代器就像一个小指针,你手动控制每一步。*it就是当前指向的字符。
Python 中的遍历
- 直接
for ch in s:和 C++ 范围 for 一样简洁。 - 用
range和下标:for i in range(len(s))然后s[i],可以同时得到索引和字符。 - 用
enumerate:for i, ch in enumerate(s)同时拿到索引和字符,最方便。
生活例子:老师查全班作业,可以:
- 按学号一个一个叫(下标)
- 直接按座位顺序收(范围 for)
- 一边叫学号一边喊名字(enumerate)
新手容易犯的错误:在 C++ 中直接用
s[i]却不检查下标是否越界。比如循环条件写成i <= s.size(),导致最后一次索引等于长度,越界访问。记住s.size()返回的是字符个数,最大下标是size()-1。
C++ 完整代码示例(带详细注释)
#include <iostream>
#include <string>
using namespace std;
int main() {
// --------------------------------------------
// 1. 构造:准备各种字符串
// --------------------------------------------
string emptyStr; // 空字符串,没有字符
string helloStr("Hello"); // 从C风格字符串构造,内容是"Hello"
string repeatedStr(5, 'A'); // 重复5个'A',得到"AAAAA"
string copyStr(helloStr); // 拷贝构造,与helloStr内容相同
cout << "emptyStr: [" << emptyStr << "]" << endl;
cout << "helloStr: " << helloStr << endl;
cout << "repeatedStr: " << repeatedStr << endl;
cout << "copyStr: " << copyStr << endl;
// --------------------------------------------
// 2. 拼接:连接字符串
// --------------------------------------------
string joined = helloStr + " " + repeatedStr; // 用+连接,不会修改helloStr和repeatedStr
cout << "joined after +: " << joined << endl; // "Hello AAAAA"
joined += "!"; // 用+=在末尾追加,joined本身被修改
cout << "joined after +=: " << joined << endl; // "Hello AAAAA!"
// append函数:直接将参数追加到调用对象末尾,并返回引用
string appended = helloStr.append(" World"); // helloStr变成"Hello World", appended也是"Hello World"(因为append返回引用)
cout << "helloStr after append: " << helloStr << endl;
cout << "appended (same as helloStr): " << appended << endl;
// --------------------------------------------
// 3. 遍历:逐个访问字符
// --------------------------------------------
string train = "ABCDE";
// 方法一:下标遍历(小心越界)
cout << "下标遍历: ";
for (int i = 0; i < (int)train.size(); i++) { // 注意size()返回无符号,最好转成int或使用size_t
cout << train[i] << " ";
}
cout << endl;
// 方法二:范围for循环(最推荐,简洁安全)
cout << "范围for遍历: ";
for (char ch : train) {
cout << ch << " ";
}
cout << endl;
// 方法三:迭代器遍历
cout << "迭代器遍历: ";
for (auto it = train.begin(); it != train.end(); ++it) {
cout << *it << " ";
}
cout << endl;
return 0;
}
Python 等价功能的代码示例(带详细注释)
# --------------------------------------------
# 1. 构造:创建字符串
# --------------------------------------------
empty_str = "" # 空字符串
hello_str = "Hello" # 直接赋值
repeated_str = 'A' * 5 # 重复字符,得到 "AAAAA"
copy_str = hello_str # 直接赋值(Python里是引用,但字符串不可变,所以修改不会影响原对象)
print(f"empty_str: [{empty_str}]")
print(f"hello_str: {hello_str}")
print(f"repeated_str: {repeated_str}")
print(f"copy_str: {copy_str}")
# --------------------------------------------
# 2. 拼接:连接字符串
# --------------------------------------------
joined = hello_str + " " + repeated_str # 用+连接,原字符串不变
print(f"joined after +: {joined}") # "Hello AAAAA"
joined += "!" # += 创建新字符串,再赋值给joined
print(f"joined after +=: {joined}") # "Hello AAAAA!"
# 大量拼接推荐用 join
parts = ["Hello", "World"]
joined_parts = " ".join(parts) # 用空格连接列表中的字符串
print(f"joined_parts from join: {joined_parts}") # "Hello World"
# --------------------------------------------
# 3. 遍历:逐个访问字符
# --------------------------------------------
train = "ABCDE"
# 方法一:直接 for 循环(最常用)
print("直接for遍历:", end=" ")
for ch in train:
print(ch, end=" ")
print()
# 方法二:用 range + 下标
print("下标遍历:", end=" ")
for i in range(len(train)):
print(train[i], end=" ")
print()
# 方法三:enumerate 同时获取索引和字符
print("enumerate遍历:", end=" ")
for i, ch in enumerate(train):
print(f"({i},{ch})", end=" ")
print()
常见错误与避坑指南
| 错误类型 | 错误示例 | 说明 | 正确做法 |
|---|---|---|---|
| C++ 下标越界 | for (int i=0; i<=s.size(); i++) 使用 s[i] | 字符串长度为 size(),最大下标是 size()-1 | 条件改为 i < s.size() |
| Python 拼接性能 | s = ''; for ch in many_chars: s += ch | 每次 += 都创建新字符串,复杂度 O(n²) | 先用列表收集,最后 ''.join(list) |
C++ 混淆 + 与 += | 以为 s1 + s2 会修改 s1 | + 返回新对象,不改变原字符串 | 想改变原字符串用 += 或 append |
| Python 字符串可变性 | 误以为可以 s[0] = 'a' | Python 字符串不可变,不能通过下标修改 | 改用列表或 s.replace 创建新字符串 |
总结与下一步
- 构造:创建字符串的不同方式,让你能应对各种初始需求。
- 拼接:把多个字符串连起来,注意 C++ 可变与 Python 不可变的区别。
- 遍历:逐个字符处理,C++ 推荐范围 for,Python 推荐直接 for 或 enumerate。
掌握了这些基础操作,你就已经能处理大部分常见任务了。接下来可以学习:
- 查找子串:在字符串里找某个单词(如
find、index) - 截取子串:取出一段(如
substr、切片) - 替换与大小写转换:修改内容(
replace、toupper) - 字符串流:用
ostringstream或join高效拼接大量字符串
还记得吗?字符串就像积木火车,你现在已经会搭车厢、连成串、再一一看过它们。继续玩下去,你会搭出更酷的火车!
例题精讲
下列选项中,哪种方式不能用于构造一个 std::string 对象?
现有 std::string a = "Hello"; std::string b = "World"; 下列哪个表达式可以正确拼接得到 "HelloWorld"?
使用下标运算符[]访问 std::string 中的字符时,如果下标超出字符串长度,程序会抛出 std::out_of_range 异常。
以下代码使用范围for循环遍历字符串并输出每个字符,请填空完成:
std::string str = "GESP";
for (char ch : ___) {
std::cout << ch << " ";
}请完成以下代码,将两个字符串拼接后赋值给第三个字符串:
std::string s1 = "Hello";
std::string s2 = "World";
std::string s3 = ___ ;
std::cout << s3; // 输出 HelloWorld