string:字符串处理
这是什么:std::string——C++ 里装文字的容器,能拼、能切、能查,内存全自动帮你管。
为什么用:上一章的 vector 只会装数字,那名字、句子、文件路径这些文字怎么办?用 char 数组手拼又长又容易越界,string 一行就能搞定,用起来和 vector 一个路数。
如何用:声明一个 std::string 就能上手,本章先把拼接、长度这些基本操作过一遍,再练 substr 切片和 find 查找。
定义与拼接
#include <iostream>
#include <string>
int main() {
std::string who = "小青蛙";
std::cout << "你好," + who + "!" << std::endl; // 拼接:直接 +
std::string word = "frog";
std::cout << word.length() << std::endl; // 长度
std::cout << word[0] << std::endl; // 首字符
return 0;
}
你会看到:
你好,小青蛙!
4
f
拼接就是 +,长度用 length(),下标从 0 起取单个字符——全是你熟悉的味道。
⚠️ 一个中文特有的坑:length() 数的是字节。UTF-8 编码里一个常用汉字占 3 个字节,所以 "小青蛙" 的 length() 是 9 不是 3。入门阶段记住这个坑,以后遇到"长度对不上",先想到它。
汉字长度实验
#include <iostream>
#include <string>
int main() {
std::string s = "小青蛙";
std::cout << s.length() << std::endl; // 9(3 个汉字 × 3 字节),不是 3
std::cout << s.size() << std::endl; // size() 和 length() 一样,也是 9
return 0;
}
切片:substr
Python 有切片,string 有 substr(起点, 个数):
#include <iostream>
#include <string>
int main() {
std::string file = "frog.png";
std::cout << file.substr(5) << std::endl; // 从下标 5 取到结尾
std::cout << file.substr(0, 4) << std::endl; // 从 0 起取 4 个
std::cout << "点在第 " << file.find(".") << " 位" << std::endl;
return 0;
}
你会看到:
png
frog
点在第 4 位
查找:find
find 返回子串第一次出现的下标;找不到时返回特殊值 npos,所以查找前先比一比:
#include <iostream>
#include <string>
int main() {
std::string file = "frog.png";
if (file.find(".png") != std::string::npos) {
std::cout << "是图片文件" << std::endl;
} else {
std::cout << "不是图片文件" << std::endl;
}
return 0;
}
你会看到:
是图片文件
查找与替换:更多用法
#include <iostream>
#include <string>
int main() {
std::string s = "hello world";
// 查找子串位置
std::cout << s.find("world") << std::endl; // 6
// 替换:从位置 6 开始,替换 5 个字符为 "cpp"
s.replace(6, 5, "cpp");
std::cout << s << std::endl; // hello cpp
// 插入:在位置 5 插入 ", "
s.insert(5, ", ");
std::cout << s << std::endl; // hello, cpp
// 删除:从位置 5 开始删除 7 个字符
s.erase(5, 7);
std::cout << s << std::endl; // hello
return 0;
}
和 char[] 的恩怨
C 语言里字符串是 char 数组,结尾还要补个 '\0',一不留神就越界;C++ 的 string 把这些全自动化了。新代码放心用 string,将来读老代码时再会会 char[] 不迟。
常用操作速查
| 操作 | 干什么 | 备注 |
|---|---|---|
s1 + s2 |
拼接 | 直接拼字面量也行 |
s.length() / s.size() |
长度(字节数) | 中文一个字占 3 字节 |
s[i] |
取第 i 个字符 | 下标从 0 起 |
s.substr(a, n) |
从 a 取 n 个;省略 n 取到尾 | 类似切片 |
s.find(t) |
找子串,返回下标 | 找不到返回 npos |
s.replace(a, n, t) |
从 a 开始替换 n 个为 t | |
s.insert(a, t) |
在位置 a 插入 t | |
s.erase(a, n) |
从 a 开始删除 n 个 |
记忆口诀
拼接用加号,长度 length 数;substr 切片,find 找不到是 npos。
❓ 常见问题
Q1:std::string 和 C 风格字符串 char[] 有什么区别?
char[] 是固定大小的数组,要手动管理 '\0' 结尾,容易越界;std::string 自动管理内存,可动态增长,有丰富的成员函数,更安全更方便。
Q2:length() 返回的是字符数还是字节数?
是字节数。一个英文字母 1 字节,一个常用汉字 UTF-8 编码 3 字节。想获取"字符数"(把一个汉字算 1 个),需要更复杂的处理,入门阶段先记住"按字节算"这个坑。
Q3:find 找不到返回什么?
返回 std::string::npos,这是一个很大的无符号数值,表示"未找到"。比较时用 != std::string::npos 判断找到,== std::string::npos 判断没找到。
⚠️ 新手翻车现场
翻车一:中文长度对不上
std::string s = "小青蛙";
std::cout << s.length(); // 打印 9,不是 3
问题:length() 数字节,3 个汉字 × 3 字节 = 9。
修正:入门阶段先记住这个坑;需要精确字符数时,可用第三方库或按字符编码逐个解码。
翻车二:find 找不到却直接当下标用
std::string s = "hello";
size_t pos = s.find("xyz"); // 返回 npos
std::cout << s[pos]; // 越界,未定义行为
问题:找不到时 pos 是 npos(一个很大的数),拿它当下标访问会越界。
修正:先判断 if (pos != std::string::npos) 再用。
翻车三:substr 参数超出长度
std::string s = "hi";
s.substr(0, 100); // 第二个参数大于剩余长度
问题:substr 允许 n 超过剩余长度,实际只取到字符串结尾,不会报错。但如果起点 a 超过长度,会抛出 out_of_range 异常。
修正:起点必须在有效范围内,n 可以大(自动截断到结尾)。
🎯 什么时候用 string
- 处理文本:拼接、切片、查找、替换
- 需要动态长度的字符序列
- 替代 C 风格的
char[],避免手动内存管理
📝 本章小结
std::string能拼(+)、切(substr)、查(find)、改(replace/insert/erase)length()/size()返回字节数,中文一个字 3 字节,记住这个坑find找不到返回npos,用前先判断- 新代码优先用
string,避免char[]的手动内存管理 - 记忆口诀:拼接用加号,长度 length 数;substr 切片,find 找不到是 npos。
动手改
- 把
file换成"yourname.jpg",分别取出主名和后缀 - 用
find找出字母o在"frog"里的位置并打印 - 拼一句自我介绍:把名字和一个年龄数字(用
std::to_string(age)转成字符串)用+连起来输出 - 用
replace把"hello world"里的world换成cpp
下一章:map 与 set,键值对和去重神器来了 🐸