C++ string 类:告别 char 数组,拥抱字符串的“智能管家“
·
🎯 还在用
char str[100]手动管理字符串?还在为strlen、strcpy、strcat提心吊胆?醒醒!C++ 的string类早就帮你把这些脏活累活全包了。本文带你从定义到操作,彻底掌握这个字符串处理的"瑞士军刀"。
📖 文章导航
- 11.1 string 类对象的定义:怎么创建一个 string?
- 11.2 string 类成员函数:增删改查,样样精通
- 11.3 string 类的操作符:用运算符直接操作字符串
- 11.4 string 类串位置指针:用迭代器精准定位
- 11.5 string 类串与 C 风格字符串的转化:新旧世界的桥梁
11.1 string 类对象的定义——七种姿势创建字符串
string 类定义在 <string> 头文件中,是 C++ 标准库提供的字符串类,比 C 风格的 char[] 安全、方便一万倍。
#include <iostream>
#include <string>
using namespace std;
int main() {
// 姿势一:空字符串
string s1;
cout << "s1: \"" << s1 << "\" 长度: " << s1.length() << endl; // "" 长度: 0
// 姿势二:用字符串字面量初始化
string s2 = "Hello, C++!";
cout << "s2: " << s2 << endl; // Hello, C++!
// 姿势三:拷贝构造
string s3(s2);
cout << "s3: " << s3 << endl; // Hello, C++!
// 姿势四:用 n 个相同字符初始化
string s4(5, '*');
cout << "s4: " << s4 << endl; // *****
// 姿势五:用另一个 string 的子串
string s5(s2, 7, 3); // 从 s2 的第7个位置开始,取3个字符
cout << "s5: " << s5 << endl; // C++
// 姿势六:C++11 列表初始化
string s6 = {'H', 'i', '!'};
cout << "s6: " << s6 << endl; // Hi!
// 姿势七:用 substr 截取(后面会详细讲)
string s7 = s2.substr(0, 5);
cout << "s7: " << s7 << endl; // Hello
return 0;
}
💡 一句话:
string的定义方式比奶茶店的点单方式还多,选你顺手的用就行。
11.2 string 类成员函数——增删改查全搞定
2.1 获取信息:长度、判空、容量
#include <iostream>
#include <string>
using namespace std;
int main() {
string s = "Hello, 你好世界!";
// 长度(两种写法等价)
cout << "length(): " << s.length() << endl; // 13(字节数)
cout << "size(): " << s.size() << endl; // 13(同上)
// 判空
string empty = "";
cout << "empty? " << empty.empty() << endl; // 1(true)
// 容量(已分配的存储空间)
cout << "capacity(): " << s.capacity() << endl; // 通常 >= size()
return 0;
}
2.2 访问字符:at、front、back、[]
string s = "Hello";
// 下标访问(不检查越界,速度快但危险)
cout << s[0] << endl; // H
cout << s[4] << endl; // o
// at() 访问(检查越界,安全但稍慢)
cout << s.at(1) << endl; // e
// cout << s.at(10); // 💥 抛出 out_of_range 异常
// 首尾字符
cout << s.front() << endl; // H(第一个字符)
cout << s.back() << endl; // o(最后一个字符)
// 修改字符
s[0] = 'h';
s.at(4) = 'O';
cout << s << endl; // hellO
⚠️ 选择建议:确定不越界用
[],不确定用at()——程序崩溃比数据损坏好得多。
2.3 拼接:push_back、append、+=
string s = "Hello";
// 追加单个字符
s.push_back('!');
cout << s << endl; // Hello!
// 追加字符串
s.append(", World");
cout << s << endl; // Hello!, World
// 用 += 追加(最常用!)
s += " 你好";
s += '!';
cout << s << endl; // Hello!, World 你好!
// 追加 n 个字符
s.append(3, '~');
cout << s << endl; // Hello!, World 你好!~~~
2.4 插入与删除:insert、erase
string s = "HelloWorld";
// 在指定位置插入
s.insert(5, ", "); // 在位置5插入
cout << s << endl; // Hello, World
// 在指定位置删除
s.erase(5, 2); // 从位置5开始删除2个字符
cout << s << endl; // HelloWorld
// 删除从位置5到末尾
s.erase(5);
cout << s << endl; // Hello
// 清空整个字符串
s.clear();
cout << "empty? " << s.empty() << endl; // 1
2.5 截取子串:substr
string s = "Hello, C++ World!";
// substr(起始位置, 长度)
string sub1 = s.substr(7, 3); // 从位置7开始取3个字符
cout << sub1 << endl; // C++
// substr(起始位置) —— 从该位置取到末尾
string sub2 = s.substr(7);
cout << sub2 << endl; // C++ World!
// 实用场景:提取文件扩展名
string filename = "report.pdf";
size_t dot = filename.rfind('.'); // 找最后一个 '.' 的位置
if (dot != string::npos) {
string ext = filename.substr(dot + 1);
cout << "扩展名: " << ext << endl; // pdf
}
2.6 查找与替换:find、rfind、replace
string s = "Hello, Hello, Hello!";
// find:从前往后找,返回第一次出现的位置
size_t pos = s.find("Hello");
cout << "第一次出现位置: " << pos << endl; // 0
// find 从指定位置开始找
pos = s.find("Hello", 1);
cout << "第二次出现位置: " << pos << endl; // 7
// rfind:从后往前找
pos = s.rfind("Hello");
cout << "最后一次出现位置: " << pos << endl; // 14
// 找不到返回 string::npos
if (s.find("World") == string::npos) {
cout << "没找到 World" << endl;
}
// 统计出现次数
int count = 0;
size_t idx = 0;
while ((idx = s.find("Hello", idx)) != string::npos) {
count++;
idx += 5; // 跳过已找到的
}
cout << "Hello 出现了 " << count << " 次" << endl; // 3 次
// replace:替换
string s2 = "Hello, World!";
s2.replace(7, 5, "C++"); // 从位置7开始,替换5个字符为 "C++"
cout << s2 << endl; // Hello, C++!
💡 查找家族速记:
find—— 从前往后找(正向)rfind—— 从后往前找(反向)find_first_of—— 找第一个匹配字符集中的任意字符find_last_of—— 找最后一个匹配字符集中的任意字符
2.7 比较:compare
string a = "apple";
string b = "banana";
// compare 返回值:< 0(a < b)、0(a == b)、> 0(a > b)
int result = a.compare(b);
if (result < 0) cout << a << " < " << b << endl; // apple < banana
else if (result == 0) cout << a << " == " << b << endl;
else cout << a << " > " << b << endl;
// 也可以直接用比较运算符(更常用!)
if (a < b) cout << "apple 排在 banana 前面" << endl;
if (a == "apple") cout << "这是苹果!" << endl;
2.8 大小写转换(实用技巧)
#include <algorithm>
string s = "Hello, World!";
// 转大写
transform(s.begin(), s.end(), s.begin(), ::toupper);
cout << s << endl; // HELLO, WORLD!
// 转小写
transform(s.begin(), s.end(), s.begin(), ::tolower);
cout << s << endl; // hello, world!
11.3 string 类的操作符——用运算符优雅地操作字符串
string 类重载了大量运算符,让你可以用最直观的方式操作字符串。
3.1 赋值操作符:=、+=
string s1 = "Hello";
string s2;
s2 = s1; // 拷贝赋值
cout << s2 << endl; // Hello
s2 += " World"; // 拼接赋值
s2 += '!';
cout << s2 << endl; // Hello World!
3.2 比较操作符:==、!=、<、>、<=、>=
string a = "abc";
string b = "abd";
string c = "abc";
cout << (a == c) << endl; // 1(true)—— 内容相同
cout << (a != b) << endl; // 1(true)—— 内容不同
cout << (a < b) << endl; // 1(true)—— 字典序:abc < abd
cout << (a > b) << endl; // 0(false)
// 比较规则:逐字符按 ASCII 码比较,类似字典排序
// 'a' < 'b',所以 "abc" < "abd"
3.3 连接操作符:+
string first = "Hello";
string second = " World";
string full = first + second + "!";
cout << full << endl; // Hello World!
// 可以和 C 风格字符串混用
string s = "C++" + string(" is ") + "awesome!";
cout << s << endl; // C++ is awesome!
// ⚠️ 两个字符串字面量不能直接用 + 连接!
// string bad = "Hello" + "World"; // ❌ 编译错误
// 因为 "Hello" 和 "World" 都是 const char*,不是 string 对象
3.4 流操作符:<<、>>
string name;
cout << "请输入你的名字: ";
cin >> name; // >> 遇到空格就停止
cout << "你好, " << name << "!" << endl;
// 用 getline 读取整行(包括空格)
cin.ignore(); // 清除缓冲区中的换行符
string sentence;
cout << "请输入一句话: ";
getline(cin, sentence);
cout << "你说的是: " << sentence << endl;
3.5 下标操作符:[]
string s = "Hello";
// 读取
cout << s[0] << endl; // H
// 修改
s[0] = 'h';
cout << s << endl; // hello
// 遍历
for (size_t i = 0; i < s.length(); i++) {
cout << s[i] << " ";
}
// h e l l o
11.4 string 类串位置指针——迭代器精准定位
迭代器是 STL 的"万能遥控器",可以用它在 string 中精确定位和操作。
4.1 迭代器基础
#include <iostream>
#include <string>
using namespace std;
int main() {
string s = "Hello, C++!";
// 正向迭代器:从头到尾
cout << "正向遍历: ";
for (string::iterator it = s.begin(); it != s.end(); ++it) {
cout << *it;
}
cout << endl; // Hello, C++!
// 反向迭代器:从尾到头
cout << "反向遍历: ";
for (string::reverse_iterator it = s.rbegin(); it != s.rend(); ++it) {
cout << *it;
}
cout << endl; // !++C ,olleH
// C++11 简化写法:auto + 范围 for
for (auto c : s) cout << c; // Hello, C++!
cout << endl;
return 0;
}
4.2 迭代器与算法配合
string s = "Hello, World!";
// 用迭代器指定范围进行操作
// 反转
auto it1 = s.begin() + 7; // 指向 'W'
auto it2 = s.begin() + 12; // 指向 '!'
reverse(it1, it2); // 只反转 "World" 部分
cout << s << endl; // Hello, dlroW!
// 用迭代器查找并插入
string s2 = "I C++!";
auto pos = s2.find("C++");
if (pos != string::npos) {
s2.insert(s2.begin() + pos, 'L'); // 在 C++ 前面插入 L
cout << s2 << endl; // I LC++!
}
// 删除指定位置的字符
string s3 = "Helloo";
s3.erase(s3.begin() + 4); // 删除下标4的字符
cout << s3 << endl; // Hello
4.3 const_iterator:只读迭代器
void printString(const string& s) {
// 只能用 const_iterator,不能修改内容
for (string::const_iterator it = s.begin(); it != s.end(); ++it) {
cout << *it;
// *it = 'X'; // ❌ 编译错误!const_iterator 不能修改
}
cout << endl;
}
int main() {
string s = "Hello";
printString(s); // Hello
}
💡 迭代器速记:
begin()/end()→ 正向(头→尾)rbegin()/rend()→ 反向(尾→头)cbegin()/cend()→ const 正向(只读)crbegin()/crend()→ const 反向(只读)
11.5 string 类串与 C 风格字符串的转化——新旧世界的桥梁
在实际开发中,你经常需要在 string 和 char* 之间转换——比如调用 C 语言的库函数、和老代码对接等。
5.1 string → C 风格字符串
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
int main() {
string s = "Hello, World!";
// 方法一:c_str()(推荐!返回 const char*)
const char* cstr1 = s.c_str();
cout << cstr1 << endl; // Hello, World!
cout << strlen(cstr1) << endl; // 13
// 方法二:data()(C++11 之前和 c_str() 相同)
const char* cstr2 = s.data();
cout << cstr2 << endl; // Hello, World!
// 方法三:拷贝到 char 数组
char buf[100];
s.copy(buf, s.length());
buf[s.length()] = '\0'; // ⚠️ copy 不会自动加 '\0',需要手动加!
cout << buf << endl; // Hello, World!
return 0;
}
⚠️ 重要警告:
c_str()返回的指针指向 string 内部的缓冲区,如果 string 被修改或销毁,该指针就失效了!
const char* p;
{
string s = "Hello";
p = s.c_str();
} // s 在这里被销毁了
// cout << p; // 💥 未定义行为!p 已经是野指针了
// 正确做法:立即拷贝
string s = "Hello";
string safe_copy(s.c_str()); // 拷贝一份
5.2 C 风格字符串 → string
// 非常简单!直接赋值或构造即可
const char* cstr = "Hello from C!";
string s1 = cstr; // 赋值
string s2(cstr); // 构造
string s3(cstr, 5); // 只取前5个字符:"Hello"
cout << s1 << endl; // Hello from C!
cout << s2 << endl; // Hello from C!
cout << s3 << endl; // Hello
5.3 数字与 string 的互转(实用技巧)
#include <string>
#include <sstream>
using namespace std;
int main() {
// ===== 数字 → string =====
// 方法一:to_string(C++11,最简单!)
int n = 42;
double d = 3.14;
string s1 = to_string(n);
string s2 = to_string(d);
cout << s1 << endl; // "42"
cout << s2 << endl; // "3.140000"
// 方法二:ostringstream(更灵活)
ostringstream oss;
oss << "值是: " << n << ", 精度: " << fixed << setprecision(2) << d;
string s3 = oss.str();
cout << s3 << endl; // "值是: 42, 精度: 3.14"
// ===== string → 数字 =====
// 方法一:stoi / stod(C++11,最简单!)
string numStr = "123";
int x = stoi(numStr);
cout << x + 1 << endl; // 124
string floatStr = "3.14";
double y = stod(floatStr);
cout << y * 2 << endl; // 6.28
// 方法二:istringstream
string data = "100 3.14 Hello";
istringstream iss(data);
int a; double b; string c;
iss >> a >> b >> c;
cout << a << " " << b << " " << c << endl; // 100 3.14 Hello
return 0;
}
实战综合案例:文本处理器
把前面学的所有 string 知识串起来,写一个实用的文本处理器:
#include <iostream>
#include <string>
#include <algorithm>
#include <sstream>
#include <vector>
using namespace std;
// 功能1:统计单词数量
int countWords(const string& text) {
istringstream iss(text);
string word;
int count = 0;
while (iss >> word) count++;
return count;
}
// 功能2:替换所有指定子串
string replaceAll(string text, const string& from, const string& to) {
size_t pos = 0;
while ((pos = text.find(from, pos)) != string::npos) {
text.replace(pos, from.length(), to);
pos += to.length();
}
return text;
}
// 功能3:提取所有邮箱地址
vector<string> extractEmails(const string& text) {
vector<string> emails;
size_t pos = 0;
while (pos < text.length()) {
size_t atPos = text.find('@', pos);
if (atPos == string::npos) break;
// 往前找邮箱开头
size_t start = atPos;
while (start > 0 && (isalnum(text[start-1]) || text[start-1] == '.' || text[start-1] == '_'))
start--;
// 往后找邮箱结尾
size_t end = atPos;
while (end < text.length() && (isalnum(text[end]) || text[end] == '.' || text[end] == '_'))
end++;
if (start < atPos && end > atPos) {
emails.push_back(text.substr(start, end - start));
}
pos = end;
}
return emails;
}
// 功能4:首字母大写
string capitalize(const string& text) {
string result = text;
bool newWord = true;
for (size_t i = 0; i < result.length(); i++) {
if (result[i] == ' ') {
newWord = true;
} else if (newWord && isalpha(result[i])) {
result[i] = toupper(result[i]);
newWord = false;
}
}
return result;
}
int main() {
string text = "hello world! this is a test. contact us at test@example.com or admin@school.edu.cn for more info.";
cout << "=== 原文 ===" << endl;
cout << text << endl;
// 统计单词数
cout << "\n单词数量: " << countWords(text) << endl;
// 替换
string replaced = replaceAll(text, "test", "demo");
cout << "\n=== 替换后 ===" << endl;
cout << replaced << endl;
// 提取邮箱
cout << "\n=== 邮箱地址 ===" << endl;
for (auto& email : extractEmails(text)) {
cout << " " << email << endl;
}
// 首字母大写
cout << "\n=== 首字母大写 ===" << endl;
cout << capitalize(text) << endl;
// 大小写转换
string upper = text;
transform(upper.begin(), upper.end(), upper.begin(), ::toupper);
cout << "\n=== 全大写 ===" << endl;
cout << upper << endl;
return 0;
}
输出:
=== 原文 ===
hello world! this is a test. contact us at test@example.com or admin@school.edu.cn for more info.
单词数量: 17
=== 替换后 ===
hello world! this is a demo. contact us at demo@example.com or admin@school.edu.cn for more info.
=== 邮箱地址 ===
test@example.com
admin@school.edu.cn
=== 首字母大写 ===
Hello World! This Is A Test. Contact Us At Test@Example.Com Or Admin@School.Edu.Cn For More Info.
=== 全大写 ===
HELLO WORLD! THIS IS A TEST. CONTACT US AT TEST@EXAMPLE.COM OR ADMIN@SCHOOL.EDU.CN FOR MORE INFO.
总结:string 类知识全景图
string 类字符串处理
│
├── 📝 定义(11.1)
│ ├── 默认构造:string s;
│ ├── 字面量构造:string s = "hello";
│ ├── 拷贝构造:string s2(s1);
│ ├── 重复字符:string s(5, '*');
│ └── 子串构造:string s(src, pos, len);
│
├── 🔧 成员函数(11.2)
│ ├── 信息:size() / length() / empty() / capacity()
│ ├── 访问:[] / at() / front() / back()
│ ├── 拼接:push_back() / append() / +=
│ ├── 插删:insert() / erase() / clear()
│ ├── 截取:substr()
│ ├── 查找:find() / rfind() / find_first_of()
│ ├── 替换:replace()
│ └── 比较:compare()
│
├── ⚡ 操作符(11.3)
│ ├── 赋值:= / +=
│ ├── 比较:== / != / < / > / <= / >=
│ ├── 连接:+
│ ├── 流:<< / >>
│ └── 下标:[]
│
├── 🔍 位置指针 / 迭代器(11.4)
│ ├── 正向:begin() / end()
│ ├── 反向:rbegin() / rend()
│ └── 只读:cbegin() / cend()
│
└── 🌉 与 C 风格字符串互转(11.5)
├── string → char*:c_str() / data()
├── char* → string:直接赋值/构造
└── 数字 ↔ string:to_string() / stoi() / stod()
🎯 一句话总结:
string类把 C 语言中那些"手动挡"的字符串操作全部升级成了"自动挡"——你只管踩油门(调函数),剩下的它帮你搞定。
如果觉得有帮助,欢迎点赞收藏 👍 有问题可以在评论区讨论!
更多推荐
所有评论(0)