C++ 标准库提供的文本处理类型——比 C 风格字符数组更安全、更灵活,是处理字符串的首选工具。
C++ 标准库提供了 string 类型,专门用来处理文字信息。它比 C 风格的字符数组(比如 char s[100])更安全、更灵活,是处理字符串的首选工具。使用前需引入 <string> 头文件。
| 1 | #include <iostream> |
| 2 | #include <string> // 必须包含这个头文件 |
| 3 | using namespace std; |
| 4 | |
| 5 | int 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 支持通过下标 [] 访问字符,但不能给还不存在的位置赋值。
读字符串有两种常见方式:只要一个单词用 cin >>,要一整行(包括空格)用 getline()。两者混用时有一个经典的坑,一定要小心。
| 1 | string word, line; |
| 2 | |
| 3 | // 情况一:读一个单词(遇到空格就停) |
| 4 | cin >> word; // 输入 "Hello World" → word 得到 "Hello" |
| 5 | |
| 6 | // 情况二:读一整行(包括空格) |
| 7 | getline(cin, line); // 输入 "Hello World" → line 得到 "Hello World" |
| 8 | |
| 9 | // ⚠️ 混用时的大坑! |
| 10 | int n; |
| 11 | cin >> n; // 输入 "42\n",读到 42,但缓冲区还剩一个 '\n' |
| 12 | cin.ignore(); // 🔑 把残留的换行符清掉 |
| 13 | getline(cin, line); // 现在才能正常读下一行 |
cin >> n 读取完数字后,你按下的回车键(\n)会残留在输入缓冲区里。紧接着 getline 一上来就看到了这个换行符,以为"哦,这行结束了",于是直接返回一个空字符串——看起来就像程序"跳过"了输入。cin 之后、getline 之前,加一句 cin.ignore(),把残留的换行符清掉即可。
cin >> str;getline(cin, str);cin 和 getline 时,务必用 cin.ignore() 清理换行符
🔗 拼接与比较 string 支持用 + 和 += 进行拼接,也支持直接用比较运算符进行字典序比较。
| 1 | string s = "Hello"; |
| 2 | s = s + " World"; // "Hello World" |
| 3 | s += "!"; // "Hello World!" |
| 4 | |
| 5 | // 字典序比较:从左到右逐字符比较 ASCII 码 |
| 6 | string a = "abc", b = "abd"; |
| 7 | cout << (a < b); // 1 (true),因为 'c' < 'd' |
| 8 | cout << (a == b); // 0 (false) |
"ab" < "abc")。
🔍 元素访问:[ ] vs at()
size() / length() 只统计这 5 个真正的字符,不包含 \0。\0 不是字符串内容的一部分,只是 C++11 起为了兼容 C 语言接口而在末尾隐藏提供的一个只读"哨兵"——可以用 s[s.size()] 读到,但不能写入,也不计入长度。
| 1 | string s = "Hello"; |
| 2 | char c1 = s[0]; // 'H',不检查越界——快速,但需自行保证安全 |
| 3 | char c2 = s.at(0); // 'H',越界时抛出异常——安全,但略有开销 |
| 4 | char c3 = s.front(); // 'H' |
| 5 | char c4 = s.back(); // 'o' |
| 方式 | 建议 |
|---|---|
| [] | 更快但不安全,越界访问是未定义行为 |
| at() | 更安全但稍慢,越界时会抛出异常提醒你 |
[],调试阶段或不确定时用 at()。✏️ 修改操作
| 1 | string s = "Hello"; |
| 2 | s.append(" World"); // "Hello World"——在末尾追加 |
| 3 | s.push_back('!'); // "Hello World!"——在末尾追加一个字符 |
| 4 | s.pop_back(); // "Hello World"——删除最后一个字符 |
| 5 | s.insert(5, " C++"); // "Hello C++ World"——在下标 5 处插入 |
| 6 | s.erase(2, 3); // 删除从下标 2 开始的 3 个字符 |
| 7 | s.replace(0, 5, "Hi"); // 把下标 0 起的 5 个字符替换为 "Hi" |
| 8 | s1.swap(s2); // 交换两个字符串的全部内容(速度极快) |
🔎 查找操作
| 1 | string s = "Hello World"; |
| 2 | size_t pos = s.find("World"); // 查找 "World",返回首次出现位置(这里是 6) |
| 3 | size_t pos2 = s.rfind("l"); // 从后往前找 'l',返回最后一次出现的位置 |
| 4 | size_t pos3 = s.find_first_of("aeiou"); // 查找元音字母第一次出现的位置 |
| 5 | |
| 6 | if (s.find("x") == string::npos) { // npos 表示"没找到" |
| 7 | cout << "没找到'x'" << endl; |
| 8 | } |
string::npos 是什么?你可以把它理解为"无效位置"。当 find() 找不到你要的东西时,就返回 string::npos 来告诉你"抱歉,没找到"。使用时只需要记住:拿 find() 的返回值和 string::npos 比较,相等就说明没找到。✂️ 截取与转换
| 1 | string s = "Hello World"; |
| 2 | string sub = s.substr(0, 5); // "Hello"——从下标 0 开始,截取 5 个字符 |
| 3 | |
| 4 | // 数字 ↔ 字符串互转(C++11 起支持) |
| 5 | string numStr = to_string(42); // 数字 → 字符串:"42" |
| 6 | int n = stoi("123"); // 字符串 → 整数:123 |
| 7 | double d = stod("3.14159"); // 字符串 → 小数:3.14159 |
🔄 迭代器遍历
把 string 想象成一排连续的格子,每个格子里放一个字符。迭代器就像夹在格子之间的"书签"——它记住了你当前所在的位置。你可以用 *it 读取当前格子的字符,用 ++it 把书签往后移一格。
| 1 | string s = "Hi"; |
| 2 | // begin() 指向第一个字符,end() 指向尾后位置 |
| 3 | for (auto it = s.begin(); it != s.end(); ++it) |
| 4 | { |
| 5 | cout << *it << " "; // 输出:H i |
| 6 | } |
| 7 | |
| 8 | // C++11 范围 for 循环(底层也是用迭代器) |
| 9 | for (char c : s) |
| 10 | { |
| 11 | cout << c; // 输出:Hi |
| 12 | } |
s.begin() 返回指向第一个字符的迭代器,s.end() 返回指向最后一个字符后面的迭代器——它不指向任何字符,只表示"已经遍历完了"。把本节出现过的方法按用途归类汇总,写代码时可以直接当参考卡用。
| 方法 | 分类 | 作用 |
|---|---|---|
| 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) | 其它 | 交换两个字符串的全部内容 |