1. C++字符串基础与初始化
在C++编程中,string类是最常用的数据类型之一,它提供了丰富的成员函数来处理文本数据。相较于C风格的字符数组,string类更加安全、灵活且易于使用。让我们从最基本的构造和初始化开始。
1.1 默认构造与初始化
创建string对象最简单的方式是使用默认构造函数:
cpp复制#include <string>
using namespace std;
string s1; // 默认构造,创建空字符串
空字符串并不是"没有内容",而是一个长度为0的有效字符串对象。在实际编程中,我们经常需要检查字符串是否为空:
cpp复制if (s1.empty()) {
cout << "字符串为空";
}
注意:使用默认构造的字符串初始状态是确定的(空字符串),这比未初始化的字符数组更安全,避免了访问随机内存的风险。
1.2 从C风格字符串初始化
C++ string可以方便地从C风格字符串(以'\0'结尾的字符数组)构造:
cpp复制const char* cstr = "Hello";
string s2(cstr); // 从指针构造
string s3 = "World"; // 直接赋值构造
这种初始化方式在混合使用C和C++代码时特别有用。但需要注意:
- 传入的指针不能为nullptr,否则会导致未定义行为
- 构造过程会复制字符内容,原C字符串的修改不会影响string对象
1.3 拷贝构造与部分字符构造
string支持从另一个string对象构造,这称为拷贝构造:
cpp复制string s4(s2); // 拷贝构造
string s5 = s2; // 等价写法
更灵活的是,我们可以只复制源字符串的一部分:
cpp复制string s6("Hello", 3); // 取前3个字符 → "Hel"
string s7(s2, 1, 3); // 从索引1开始取3个 → "ell"
技巧:部分构造在处理网络协议或文件格式时特别有用,比如解析固定长度的字段。
1.4 重复字符与初始化列表
创建由重复字符组成的字符串:
cpp复制string s8(5, 'a'); // 5个'a' → "aaaaa"
C++11引入了初始化列表语法,可以直接用字符列表构造:
cpp复制string s9{'h', 'e', 'l', 'l', 'o'}; // 等同于"hello"
2. 字符串赋值与修改
2.1 基本赋值操作
string支持多种赋值方式:
cpp复制string s;
s = "hello"; // 直接赋值
s = string("world"); // 显式构造后赋值
s = 'a'; // 单个字符赋值
赋值操作会替换字符串的全部内容,并自动管理内存,无需担心原内容的释放问题。
2.2 assign()方法
assign()提供了更灵活的赋值方式:
cpp复制s.assign("hello"); // 直接赋值
s.assign("hello", 3); // 取前3个字符 → "hel"
s.assign(5, 'x'); // 5个'x' → "xxxxx"
s.assign(s2, 1, 2); // 从s2索引1开始取2个字符
assign()的一个优势是可以精确控制复制的范围和内容,在处理二进制数据或特定格式文本时特别有用。
2.3 swap()方法
交换两个字符串内容的高效方式:
cpp复制string a = "apple", b = "banana";
a.swap(b); // 现在a="banana", b="apple"
性能提示:swap()通常只是交换内部指针,不涉及实际内容的复制,因此效率极高,适合大字符串交换。
3. 访问字符串内容
3.1 下标访问与at()方法
访问字符串中的单个字符有两种主要方式:
cpp复制string s = "hello";
char c1 = s[0]; // 'h',使用下标操作符
s[0] = 'H'; // 修改第一个字符
下标访问速度快但不检查边界,越界访问会导致未定义行为。更安全的方式是使用at():
cpp复制char c2 = s.at(1); // 'e',带边界检查
at()在越界时会抛出std::out_of_range异常,适合在不确定索引是否有效时使用。
3.2 首尾字符访问
C++11引入了更直观的首尾字符访问方式:
cpp复制char first = s.front(); // 等价于s[0]
char last = s.back(); // 等价于s[s.size()-1]
3.3 迭代器访问
string支持标准库风格的迭代器:
cpp复制// 正向迭代器
for (auto it = s.begin(); it != s.end(); ++it) {
cout << *it;
}
// 反向迭代器
for (auto it = s.rbegin(); it != s.rend(); ++it) {
cout << *it;
}
迭代器提供了统一的容器访问接口,可以与标准库算法配合使用。
4. 字符串拼接
4.1 +运算符与+=运算符
最直观的拼接方式是使用+和+=:
cpp复制string s1 = "hello", s2 = "world";
string s3 = s1 + " " + s2; // "hello world"
s1 += "!"; // s1变为"hello!"
性能注意:连续的+操作会产生临时对象,对于大量拼接建议使用append()或stringstream。
4.2 append()方法
append()提供了更灵活的拼接方式:
cpp复制s1.append(s2); // 直接拼接
s1.append("abc", 2); // 拼接"abc"的前2个字符
s1.append(3, 'x'); // 拼接3个'x'
s1.append(s2, 1, 2); // 拼接s2从索引1开始的2个字符
4.3 push_back()方法
添加单个字符最高效的方式:
cpp复制s1.push_back('!');
5. 字符串比较
5.1 比较运算符
string重载了所有比较运算符,按字典序比较:
cpp复制string a = "apple", b = "banana";
if (a < b) cout << "a在b前面"; // 输出
比较是基于字符的ASCII值逐个进行的,大小写敏感。
5.2 compare()方法
需要更详细比较结果时使用:
cpp复制int res = a.compare(b); // 返回负数、0或正数
compare()还支持部分比较:
cpp复制// 比较a从1开始的3个字符和b从0开始的2个字符
res = a.compare(1, 3, b, 0, 2);
6. 字符串查找
6.1 find()与rfind()
查找子串或字符:
cpp复制string s = "hello world hello";
size_t pos = s.find("hello"); // 返回0
pos = s.rfind("hello"); // 从后往前找,返回12
找不到时返回string::npos,这是一个特殊值(通常是size_t的最大值)。
6.2 查找字符集合
查找属于或不属于某字符集合的第一个字符:
cpp复制pos = s.find_first_of("aeiou"); // 找第一个元音
pos = s.find_first_not_of("helo"); // 找第一个非h/e/l/o字符
这些方法在解析特定格式文本时非常有用。
7. 字符串修改操作
7.1 substr()截取子串
cpp复制string s = "hello world";
string sub = s.substr(0, 5); // "hello"
7.2 replace()替换
cpp复制s.replace(6, 5, "C++"); // "hello C++"
7.3 insert()插入
cpp复制s.insert(5, " world"); // "hello world"
7.4 erase()删除
cpp复制s.erase(5, 6); // 删除" world" → "hello"
8. 字符串与数值转换
8.1 数值转字符串
cpp复制string s = to_string(123); // "123"
8.2 字符串转数值
cpp复制int i = stoi("42");
double d = stod("3.14");
9. 实用技巧
9.1 去除首尾空白
cpp复制string trim(string s) {
s.erase(0, s.find_first_not_of(" \t\n\r"));
s.erase(s.find_last_not_of(" \t\n\r") + 1);
return s;
}
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 解析HTTP请求行
cpp复制string req_line = "GET /index.html HTTP/1.1";
istringstream iss(req_line);
string method, uri, version;
iss >> method >> uri >> version;
在实际项目中,合理运用string的各种方法可以大幅提高开发效率和代码质量。特别是在网络编程、文本处理和协议解析等场景���,熟练掌握string操作是C++开发者的基本功。
