首页 / 第十一章 · STL 标准模板库 / 11.1 string

11.1 string 字符串

C++ 标准库提供的文本处理类型——比 C 风格字符数组更安全、更灵活,是处理字符串的首选工具。

本页目录

C++ 标准库提供了 string 类型,专门用来处理文字信息。它比 C 风格的字符数组(比如 char s[100])更安全、更灵活,是处理字符串的首选工具。使用前需引入 <string> 头文件。

11.1.1 定义与初始化
C++ · string 的几种初始化方式
1#include <iostream>
2#include <string> // 必须包含这个头文件
3using namespace std;
4
5int main()
6{
7 string s1; // 空字符串
8 string s2 = "Hello"; // 从 C 风格字符串初始化
9 string s3(s2); // 拷贝构造
10 string s4(5, 'a'); // "aaaaa"——5 个字符 'a'
11 // string s5 = "Hello"s; // C++14 字面量语法,需 using namespace std::string_literals;
12
13 return 0;
14}
⚠️
注意:
· 未初始化的 string 默认为空字符串(长度为 0),不会像字符数组那样出现乱码。
· 虽然 string 支持通过下标 [] 访问字符,但不能给还不存在的位置赋值
11.1.2 输入与输出

读字符串有两种常见方式:只要一个单词用 cin >>,要一整行(包括空格)用 getline()。两者混用时有一个经典的坑,一定要小心。

C++ · cin 与 getline 的区别
1string word, line;
2
3// 情况一:读一个单词(遇到空格就停)
4cin >> word; // 输入 "Hello World" → word 得到 "Hello"
5
6// 情况二:读一整行(包括空格)
7getline(cin, line); // 输入 "Hello World" → line 得到 "Hello World"
8
9// ⚠️ 混用时的大坑!
10int n;
11cin >> n; // 输入 "42\n",读到 42,但缓冲区还剩一个 '\n'
12cin.ignore(); // 🔑 把残留的换行符清掉
13getline(cin, line); // 现在才能正常读下一行
📖
为什么混用会出问题?
cin >> n 读取完数字后,你按下的回车键(\n)会残留在输入缓冲区里。紧接着 getline 一上来就看到了这个换行符,以为"哦,这行结束了",于是直接返回一个空字符串——看起来就像程序"跳过"了输入。

解决办法:cin 之后、getline 之前,加一句 cin.ignore(),把残留的换行符清掉即可。
💡
选择指南:
· 只读单个单词(无空格)→ cin >> str;
· 需要读含空格的整行 → getline(cin, str);
· 混用 cingetline 时,务必用 cin.ignore() 清理换行符
11.1.3 常用操作

🔗 拼接与比较 string 支持用 ++= 进行拼接,也支持直接用比较运算符进行字典序比较。

C++ · 拼接与字典序比较
1string s = "Hello";
2s = s + " World"; // "Hello World"
3s += "!"; // "Hello World!"
4
5// 字典序比较:从左到右逐字符比较 ASCII 码
6string a = "abc", b = "abd";
7cout << (a < b); // 1 (true),因为 'c' < 'd'
8cout << (a == b); // 0 (false)
📐
字典序规则:从左到右逐个字符比较,遇到第一个不同字符时,ASCII 值小的字符串更小;若一方为另一方的前缀,则较短的更小(如 "ab" < "abc")。

🔍 元素访问:[ ] vs at()

字符串 "Hello" 的内存格子 size() = 5
H
[0]
e
[1]
l
[2]
l
[3]
o
[4]
\0
[5]
size() / length() 只统计这 5 个真正的字符,不包含 \0\0 不是字符串内容的一部分,只是 C++11 起为了兼容 C 语言接口而在末尾隐藏提供的一个只读"哨兵"——可以用 s[s.size()] 读到,但不能写入,也不计入长度。
C++ · 元素访问
1string s = "Hello";
2char c1 = s[0]; // 'H',不检查越界——快速,但需自行保证安全
3char c2 = s.at(0); // 'H',越界时抛出异常——安全,但略有开销
4char c3 = s.front(); // 'H'
5char c4 = s.back(); // 'o'
方式建议
[]更快但不安全,越界访问是未定义行为
at()更安全但稍慢,越界时会抛出异常提醒你
🎯
建议:竞赛中确定不越界时用 [],调试阶段或不确定时用 at()

✏️ 修改操作

C++ · 增删改字符串
1string s = "Hello";
2s.append(" World"); // "Hello World"——在末尾追加
3s.push_back('!'); // "Hello World!"——在末尾追加一个字符
4s.pop_back(); // "Hello World"——删除最后一个字符
5s.insert(5, " C++"); // "Hello C++ World"——在下标 5 处插入
6s.erase(2, 3); // 删除从下标 2 开始的 3 个字符
7s.replace(0, 5, "Hi"); // 把下标 0 起的 5 个字符替换为 "Hi"
8s1.swap(s2); // 交换两个字符串的全部内容(速度极快)

🔎 查找操作

C++ · find / rfind / find_first_of
1string s = "Hello World";
2size_t pos = s.find("World"); // 查找 "World",返回首次出现位置(这里是 6)
3size_t pos2 = s.rfind("l"); // 从后往前找 'l',返回最后一次出现的位置
4size_t pos3 = s.find_first_of("aeiou"); // 查找元音字母第一次出现的位置
5
6if (s.find("x") == string::npos) { // npos 表示"没找到"
7 cout << "没找到'x'" << endl;
8}
📖
string::npos 是什么?你可以把它理解为"无效位置"。当 find() 找不到你要的东西时,就返回 string::npos 来告诉你"抱歉,没找到"。使用时只需要记住:拿 find() 的返回值和 string::npos 比较,相等就说明没找到。

✂️ 截取与转换

C++ · substr 与数字互转
1string s = "Hello World";
2string sub = s.substr(0, 5); // "Hello"——从下标 0 开始,截取 5 个字符
3
4// 数字 ↔ 字符串互转(C++11 起支持)
5string numStr = to_string(42); // 数字 → 字符串:"42"
6int n = stoi("123"); // 字符串 → 整数:123
7double d = stod("3.14159"); // 字符串 → 小数:3.14159

🔄 迭代器遍历

string 想象成一排连续的格子,每个格子里放一个字符。迭代器就像夹在格子之间的"书签"——它记住了你当前所在的位置。你可以用 *it 读取当前格子的字符,用 ++it 把书签往后移一格。

C++ · 用迭代器和范围 for 遍历
1string s = "Hi";
2// begin() 指向第一个字符,end() 指向尾后位置
3for (auto it = s.begin(); it != s.end(); ++it)
4{
5 cout << *it << " "; // 输出:H i
6}
7
8// C++11 范围 for 循环(底层也是用迭代器)
9for (char c : s)
10{
11 cout << c; // 输出:Hi
12}
📌
小贴士:s.begin() 返回指向第一个字符的迭代器,s.end() 返回指向最后一个字符后面的迭代器——它不指向任何字符,只表示"已经遍历完了"。
📋 string 常用操作速查表

把本节出现过的方法按用途归类汇总,写代码时可以直接当参考卡用。

方法分类作用
s + s2 / s += s2拼接拼接两个字符串
== / != / < / >比较按字典序比较
s[i]访问下标访问,不检查越界,速度快
s.at(i)访问下标访问,越界抛异常,更安全
s.front() / s.back()访问第一个 / 最后一个字符
s.append(t)修改在末尾追加字符串
s.push_back(c) / s.pop_back()修改末尾追加 / 删除一个字符
s.insert(pos, t)修改在 pos 处插入字符串
s.erase(pos, len)修改删除 pos 起的 len 个字符
s.replace(pos, len, t)修改替换 pos 起的 len 个字符为 t
s.find(t)查找查找 t 首次出现的位置,找不到返回 string::npos
s.rfind(t)查找从后往前查找 t 最后一次出现的位置
s.find_first_of(chars)查找查找 chars 中任意字符首次出现的位置
s.substr(pos, len)截取从 pos 开始截取 len 个字符
to_string(num)转换数字 → 字符串
stoi(s) / stod(s)转换字符串 → 整数 / 小数
s.size() / s.empty()容量长度 / 判断是否为空
s.clear()容量清空字符串
s.begin() / s.end()遍历正向迭代器范围
s1.swap(s2)其它交换两个字符串的全部内容