1. 什么是string?
string是C++标准库中提供的一个字符串类,它封装了字符序列的操作,提供了丰富的成员函数来处理字符串。与C风格的字符数组(char[])相比,string类更加安全、方便,并且自动管理内存。
string类的定义在
cpp复制typedef basic_string<char> string;
string对象内部维护了一个动态分配的字符数组,可以根据需要自动调整大小。这使得我们不必像使用C风格字符串那样担心缓冲区溢出等问题。
注意:虽然string类很方便,但在与C风格API交互时,可能需要通过c_str()方法获取内部的C风格字符串指针。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. string的基本操作
2.1 创建和初始化string对象
创建string对象有多种方式:
cpp复制string s1; // 空字符串
string s2("hello"); // 用C风格字符串初始化
string s3 = "world"; // 赋值初始化
string s4(5, 'a'); // 包含5个'a'的字符串
string s5(s2); // 拷贝构造
2.2 字符串赋值
string支持多种赋值方式:
cpp复制string s;
s = "hello"; // 用C风格字符串赋值
s = 'A'; // 用单个字符赋值
s = otherString; // 用另一个string对象赋值
s.assign("hello"); // 使用assign方法
s.assign(5, 'x'); // 赋值5个'x'
2.3 字符串连接
string提供了多种连接字符串的方式:
cpp复制string s1 = "hello";
string s2 = "world";
string s3 = s1 + " " + s2; // 使用+运算符
s1 += " "; // 使用+=运算符
s1.append(s2); // 使用append方法
3. string的常用方法
3.1 访问字符
可以通过下标运算符[]或at()方法访问单个字符:
cpp复制string s = "hello";
char c1 = s[1]; // 'e'
char c2 = s.at(1); // 'e'
注意:at()方法会进行边界检查,如果索引越界会抛出out_of_range异常,而[]运算符不会检查。
3.2 字符串比较
string支持多种比较方式:
cpp复制string s1 = "apple";
string s2 = "banana";
if (s1 == s2) {...} // 相等比较
if (s1 < s2) {...} // 小于比较
if (s1.compare(s2) < 0) {...} // 使用compare方法
compare方法返回:
- 0:字符串相等
- 负数:当前字符串小于参数字符串
- 正数:当前字符串大于参数字符串
3.3 字符串查找
string提供了多种查找方法:
cpp复制string s = "hello world";
size_t pos;
pos = s.find("world"); // 查找子串
pos = s.find('o'); // 查找字符
pos = s.find('o', 5); // 从位置5开始查找
pos = s.rfind('o'); // 反向查找
如果查找失败,这些方法会返回string::npos。
3.4 子串操作
可以使用substr方法获取子串:
cpp复制string s = "hello world";
string sub = s.substr(6, 5); // 从位置6开始,取5个字符,"world"
3.5 字符串修改
string提供了多种修改方法:
cpp复制string s = "hello";
s.insert(5, " world"); // 插入
s.erase(5, 6); // 删除
s.replace(0, 5, "hi"); // 替换
4. string与其它类型的转换
4.1 与数值类型的转换
C++11引入了方便的字符串与数值转换方法:
cpp复制// 字符串转数值
int i = stoi("42");
double d = stod("3.14");
// 数值转字符串
string s1 = to_string(42);
string s2 = to_string(3.14);
4.2 与C风格字符串的转换
string与C风格字符串的相互转换:
cpp复制// string转C风格字符串
string s = "hello";
const char* cstr = s.c_str();
// C风格字符串转string
const char* cstr = "world";
string s(cstr);
4.3 与字符数组的转换
如果需要处理二进制数据,可以使用data()方法:
cpp复制string s = "hello";
char* data = s.data(); // 可修改的字符数组
5. string的高级用法
5.1 迭代器
string支持迭代器,可以方便地遍历字符串:
cpp复制string s = "hello";
for (auto it = s.begin(); it != s.end(); ++it) {
cout << *it;
}
C++11还提供了范围for循环:
cpp复制for (char c : s) {
cout << c;
}
5.2 容量管理
string提供了一些容量管理方法:
cpp复制string s;
s.reserve(100); // 预分配空间
s.shrink_to_fit(); // 缩减容量以适应大小
cout << s.capacity(); // 当前容量
cout << s.size(); // 当前大小
5.3 字符串流
可以使用stringstream进行字符串的格式化操作:
cpp复制#include <sstream>
stringstream ss;
ss << "The answer is " << 42;
string s = ss.str();
6. string的性能考虑
6.1 小字符串优化
许多实现会使用小字符串优化(SSO),即对于较短的字符串,直接将其存储在对象内部,而不进行堆分配。这可以显著提高小字符串的性能。
6.2 连接操作的效率
频繁使用+运算符连接字符串会导致多次内存分配和拷贝。对于大量连接操作,更高效的方式是:
cpp复制string result;
result.reserve(total_length); // 预分配足够空间
result += s1;
result += s2;
// ...
或者使用ostringstream:
cpp复制ostringstream oss;
oss << s1 << s2 << s3;
string result = oss.str();
6.3 移动语义
C++11引入了移动语义,可以高效地转移string的所有权:
cpp复制string createString() {
string s(1000000, 'x'); // 大字符串
return s; // 移动而非拷贝
}
string s = createString(); // 高效
7. 常见问题与解决方案
7.1 中文处理问题
string是基于char的,对于多字节字符(如UTF-8编码的中文)需要特别注意:
cpp复制string s = "你好";
cout << s.length(); // 返回的是字节数,不是字符数
如果需要正确处理Unicode字符,可以考虑使用wstring(宽字符字符串)或第三方库如ICU。
7.2 内存管理
虽然string自动管理内存,但在某些情况下仍需注意:
cpp复制const char* func() {
string s = "temporary";
return s.c_str(); // 错误!s被销毁后指针无效
}
7.3 线程安全
标准规定:
- 对同一string对象的并发读取是安全的
- 对同一string对象的并发写入或读写混合是不安全的
需要同步机制来保证线程安全。
8. string与其他语言字符串的对比
8.1 与C# string比较
C#的string是不可变的,每次修改都会创建新对象。而C++的string是可变的,直接修改原对象。
8.2 与Python str比较
Python的str也是不可变的,类似于C#。Python的str支持更丰富的Unicode处理功能。
8.3 与Java String比较
Java的String也是不可变的,但提供了StringBuilder/StringBuffer用于高效构建字符串。
9. 实际应用示例
9.1 文件路径处理
cpp复制string path = "/usr/local/bin";
size_t pos = path.find_last_of('/');
if (pos != string::npos) {
string dir = path.substr(0, pos);
string file = path.substr(pos + 1);
}
9.2 字符串分割
cpp复制vector<string> split(const string& s, char delim) {
vector<string> tokens;
string token;
istringstream tokenStream(s);
while (getline(tokenStream, token, delim)) {
tokens.push_back(token);
}
return tokens;
}
9.3 字符串格式化
cpp复制string format(const string& pattern, const vector<string>& args) {
string result = pattern;
for (size_t i = 0; i < args.size(); ++i) {
string placeholder = "{" + to_string(i) + "}";
size_t pos = result.find(placeholder);
if (pos != string::npos) {
result.replace(pos, placeholder.length(), args[i]);
}
}
return result;
}
10. 最佳实践建议
- 尽量使用string代替C风格字符串,除非有特殊需求
- 对于大量字符串连接操作,考虑使用ostringstream或预分配空间
- 在多线程环境中访问string时,确保适当的同步
- 处理多字节字符时,注意length()和size()返回的是字节数而非字符数
- 与C API交互时,注意c_str()返回的指针的生命周期
- 考虑使用C++17的string_view来避免不必要的字符串拷贝
