012 C++的string处理详解
作者Lou Xiao, deepseek创建时间2025-04-02 07:47:06更新时间2025-04-02 07:47:06
1. 基本概念与定义
1.1 C++字符串类型
C++中有两种主要的字符串表示方式:
1. C风格字符串:字符数组,以空字符('\0')结尾
2. std::string:C++标准库提供的字符串类,封装了字符串操作
1.2 std::string简介
std::string
是C++标准库中定义的字符串类,属于STL的一部分,定义在<string>
头文件中。它是basic_string<char>
的typedef。
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
#include <string>
2
using std::string;
2. std::string的基本用法
2.1 构造与初始化
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
string s1; // 默认构造,空字符串
2
string s2("Hello"); // 从C风格字符串构造
3
string s3 = "World"; // 赋值初始化
4
string s4(s2); // 拷贝构造
5
string s5(5, 'a'); // 填充构造,5个'a'
6
string s6 = {'H', 'i'}; // 初始化列表
7
string s7(s2, 1, 3); // 子串构造,从位置1开始,长度3("ell")
2.2 赋值操作
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
string s;
2
s = "Hello"; // 赋值C风格字符串
3
s = 'A'; // 赋值单个字符
4
s = other_string; // 赋值另一个string对象
5
s.assign(5, 'x'); // 赋值为5个'x'
6
s.assign("Hello", 3); // 赋值为前3个字符("Hel")
2.3 访问元素
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
string s = "Hello";
2
char c1 = s[1]; // 'e',不检查边界
3
char c2 = s.at(1); // 'e',会检查边界,越界抛出异常
4
char c3 = s.front(); // 'H',第一个字符
5
char c4 = s.back(); // 'o',最后一个字符
2.4 容量操作
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
string s = "Hello";
2
s.size(); // 5,字符数量
3
s.length(); // 5,与size()相同
4
s.capacity(); // 当前分配的存储空间
5
s.reserve(100);// 预分配空间
6
s.shrink_to_fit(); // 减少capacity以匹配size
7
s.empty(); // 是否为空
2.5 修改操作
2.5.1 拼接
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
string s = "Hello";
2
s += " World"; // "Hello World"
3
s.append("!!!"); // "Hello World!!!"
4
s.push_back('!'); // 追加单个字符
5
s.insert(5, " C++"); // "Hello C++ World!!!!"
2.5.2 删除
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
string s = "Hello World";
2
s.erase(5, 6); // 从位置5删除6个字符 -> "Hello"
3
s.pop_back(); // 删除最后一个字符
4
s.clear(); // 清空字符串
2.5.3 替换
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
string s = "Hello World";
2
s.replace(6, 5, "C++"); // "Hello C++"
2.6 字符串操作
2.6.1 子串
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
string s = "Hello World";
2
string sub = s.substr(6, 5); // "World"
2.6.2 查找
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
string s = "Hello World";
2
size_t pos = s.find("World"); // 6
3
pos = s.find('o'); // 4
4
pos = s.find('o', 5); // 从位置5开始找 -> 7
5
pos = s.rfind('o'); // 从后向前找 -> 7
6
pos = s.find_first_of("aeiou"); // 第一个元音位置 -> 1('e')
7
pos = s.find_last_of("aeiou"); // 最后一个元音位置 -> 7('o')
8
pos = s.find_first_not_of("Helo Wrd"); // 找不到返回string::npos
2.6.3 比较
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
string s1 = "apple";
2
string s2 = "banana";
3
int res = s1.compare(s2); // <0 (s1 < s2)
4
res = s1.compare("apple"); // 0 (相等)
5
res = s1.compare(0, 3, "app"); // 比较前3个字符
2.7 数值转换
C++11引入了方便的数值与字符串转换函数:
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
// 字符串转数值
2
string s = "123.45";
3
int i = stoi(s); // 123
4
double d = stod(s); // 123.45
5
long l = stol(s); // 123
6
7
// 数值转字符串
8
string s1 = to_string(123); // "123"
9
string s2 = to_string(123.456); // "123.456000"
3. 现代C++实践 (C++11/14/17/20)
3.1 字符串字面量后缀
C++14引入了用户定义的字面量后缀:
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
using namespace std::string_literals;
2
3
auto s = "Hello"s; // std::string类型
4
auto s2 = u8"Hello"s; // UTF-8字符串
5
auto s3 = L"Hello"s; // wstring
6
auto s4 = u"Hello"s; // u16string
7
auto s5 = U"Hello"s; // u32string
3.2 字符串视图 (C++17)
std::string_view
提供对字符串的非拥有视图,避免不必要的拷贝:
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
#include <string_view>
2
3
void process(std::string_view sv) {
4
// 可以接受string、char*、字符串字面量等
5
auto sub = sv.substr(2, 5);
6
// ...
7
}
8
9
process("Hello World"); // 不创建临时string
10
process(some_string); // 不拷贝
3.3 字符串连接优化
现代编译器会对字符串字面量连接进行优化:
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
auto s = "Hello" " " "World"; // 编译时连接
3.4 格式化字符串 (C++20)
C++20引入了std::format
,提供类似Python的字符串格式化:
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
#include <format>
2
3
string s = std::format("Hello {}!", "World"); // "Hello World!"
4
double pi = 3.1415926;
5
string s2 = std::format("{:.2f}", pi); // "3.14"
4. 常见错误与陷阱
4.1 C风格字符串与std::string混淆
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
// 错误示例
2
const char* cstr = "Hello";
3
string s = cstr + " World"; // 指针算术错误
4
5
// 正确做法
6
string s = string(cstr) + " World";
4.2 迭代器失效
修改字符串可能导致迭代器失效:
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
string s = "Hello";
2
auto it = s.begin();
3
s += " World"; // 可能导致重新分配
4
*it = 'h'; // 未定义行为
4.3 未检查find结果
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
string s = "Hello";
2
size_t pos = s.find("World");
3
string sub = s.substr(pos); // pos == string::npos,抛出异常
4
5
// 正确做法
6
if (pos != string::npos) {
7
string sub = s.substr(pos);
8
}
4.4 编码问题
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
string s = "你好"; // 依赖于执行字符集
2
// 更好的做法
3
string s = u8"你好"; // UTF-8编码
4.5 性能陷阱
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
// 低效的字符串拼接
2
string s;
3
for (int i = 0; i < 10000; ++i) {
4
s += "a"; // 可能导致多次重新分配
5
}
6
7
// 更高效的做法
8
string s;
9
s.reserve(10000);
10
for (int i = 0; i < 10000; ++i) {
11
s += "a";
12
}
5. 最佳实践
- 优先使用std::string而非C风格字符串
- 预分配空间:对于大量拼接操作,使用reserve()
- 使用string_view:对于只读字符串参数
- 注意编码:明确指定字符串编码(u8, L, u, U前缀)
- 避免不必要的拷贝:使用引用或移动语义
- 使用现代C++特性:字面量后缀、格式化等
- 检查边界:特别是at()和substr()操作
- 考虑异常安全:特别是内存分配可能失败的情况
6. 性能考虑
- 小字符串优化(SSO):大多数实现对小字符串(通常≤15字符)有特殊优化
- COW(Copy-On-Write):旧版C++标准允许,C++11后禁止
- 移动语义:C++11后,string支持移动构造和移动赋值
- 内存分配:频繁的字符串操作可能导致多次重新分配
7. 扩展主题
7.1 Unicode处理
C++标准库对Unicode支持有限,可考虑第三方库如ICU
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
// 宽字符字符串
2
std::wstring ws = L"宽字符";
3
// UTF-8字符串 (C++20)
4
std::u8string u8s = u8"UTF-8字符串";
7.2 正则表达式
C++11引入了<regex>
库:
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
#include <regex>
2
string s = "Hello 123 World";
3
std::regex pattern(R"(\d+)");
4
smatch matches;
5
if (regex_search(s, matches, pattern)) {
6
cout << matches[0]; // "123"
7
}
7.3 字符串分割
标准库没有直接提供,可以:
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
vector<string> split(const string& s, char delim) {
2
vector<string> tokens;
3
string token;
4
istringstream tokenStream(s);
5
while (getline(tokenStream, token, delim)) {
6
tokens.push_back(token);
7
}
8
return tokens;
9
}
7.4 字符串算法
<algorithm>
中的算法可用于字符串:
1.双击鼠标左键复制此行;2.单击复制所有代码。
1
string s = "Hello";
2
transform(s.begin(), s.end(), s.begin(), ::toupper); // "HELLO"
3
sort(s.begin(), s.end()); // "EHLLO"
8. 总结
C++的字符串处理功能强大但需要谨慎使用。现代C++提供了更安全、更高效的字符串操作方式。理解std::string的内部机制和性能特征对于编写高效代码至关重要。对于复杂的文本处理任务,可能需要结合标准库功能和第三方库来实现最佳效果。
附录:主要API表格
以下是 std::string
的完整 API 整理表格,涵盖 构造、容量、访问、修改、查找、比较、迭代器、数值转换 等核心功能,并提供 名称、用途、原型、代码示例、说明。
1. 构造函数
名称 | 用途 | 原型 | 代码示例 | 说明 |
---|---|---|---|---|
string() | 默认构造(空字符串) | string(); | std::string s1; | 创建一个空字符串。 |
string(const char*) | 从 C 字符串构造 | string(const char* s); | std::string s2("hello"); | 拷贝 \0 结尾的 C 字符串。 |
string(size_t, char) | 填充构造 | string(size_t n, char c); | std::string s3(5, 'a'); // "aaaaa" | 用 n 个字符 c 初始化。 |
string(const string&) | 拷贝构造 | string(const string& str); | std::string s4(s3); | 深拷贝另一个 string 。 |
string(string&&) | 移动构造(C++11) | string(string&& str) noexcept; | std::string s5(std::move(s4)); | 高效转移所有权,原字符串被置空。 |
2. 容量操作
名称 | 用途 | 原型 | 代码示例 | 说明 |
---|---|---|---|---|
size() | 返回字符数 | size_t size() const noexcept; | std::string s = "abc"; s.size(); // 3 | 等价于 length() 。 |
length() | 返回字符数 | size_t length() const noexcept; | s.length(); // 3 | 与 size() 相同。 |
capacity() | 返回当前分配的存储空间 | size_t capacity() const noexcept; | s.capacity(); // >=3 | 可能大于 size() 。 |
reserve() | 预分配内存 | void reserve(size_t n = 0); | s.reserve(100); | 避免频繁重新分配内存。 |
shrink_to_fit() | 释放多余内存(C++11) | void shrink_to_fit(); | s.shrink_to_fit(); | 使 capacity() 接近 size() 。 |
empty() | 检查是否为空 | bool empty() const noexcept; | if (s.empty()) { ... } | 等价于 size() == 0 。 |
3. 元素访问
名称 | 用途 | 原型 | 代码示例 | 说明 |
---|---|---|---|---|
operator[] | 访问指定位置字符(不检查越界) | char& operator[](size_t pos); const char& operator[](size_t pos) const; | char c = s[0]; // 'a' | 越界行为未定义。 |
at() | 访问指定位置字符(检查越界) | char& at(size_t pos); const char& at(size_t pos) const; | char c = s.at(0); // 越界抛出 std::out_of_range | 安全但性能略低。 |
front() | 访问首字符 | char& front(); const char& front() const; | s.front() = 'A'; | 等价于 s[0] 。 |
back() | 访问末尾字符 | char& back(); const char& back() const; | s.back() = 'Z'; | 等价于 s[s.size()-1] 。 |
data() | 返回底层字符数组(可能无 \0 ) | const char* data() const noexcept; | const char* p = s.data(); | C++11 后保证以 \0 结尾(同 c_str() )。 |
c_str() | 返回 C 风格字符串(带 \0 ) | const char* c_str() const noexcept; | printf("%s", s.c_str()); | 保证以 \0 结尾。 |
4. 修改操作
名称 | 用途 | 原型 | 代码示例 | 说明 |
---|---|---|---|---|
operator= | 赋值操作 | string& operator=(const string& str); string& operator=(const char* s); | s = "new"; | 支持 string 、C 字符串、字符等。 |
assign() | 赋值内容 | string& assign(const char* s); string& assign(size_t n, char c); | s.assign(3, 'x'); // "xxx" | 功能类似构造函数。 |
append() | 追加内容 | string& append(const string& str); string& append(const char* s); | s.append("123"); | 支持多种参数类型。 |
push_back() | 追加单个字符 | void push_back(char c); | s.push_back('!'); | 效率高于 append 追加单字符。 |
insert() | 插入内容 | string& insert(size_t pos, const string& str); | s.insert(1, "abc"); | 在 pos 处插入字符串或字符。 |
erase() | 删除字符 | string& erase(size_t pos = 0, size_t len = npos); | s.erase(1, 2); // 删除从位置1开始的2个字符 | 默认删除到末尾。 |
replace() | 替换部分字符 | string& replace(size_t pos, size_t len, const string& str); | s.replace(0, 1, "X"); // 替换首字符为 'X' | 支持多种参数类型。 |
clear() | 清空字符串 | void clear() noexcept; | s.clear(); // s变为空 | 等价于 s.erase() 。 |
swap() | 交换两个字符串 | void swap(string& str) noexcept; | std::string a("A"), b("B"); a.swap(b); | 高效交换内容(不重新分配内存)。 |
5. 查找与比较
名称 | 用途 | 原型 | 代码示例 | 说明 |
---|---|---|---|---|
find() | 查找子串或字符 | size_t find(const string& str, size_t pos = 0) const; | s.find("lo"); // 返回首次出现的位置 | 失败返回 npos 。 |
rfind() | 反向查找 | size_t rfind(const string& str, size_t pos = npos) const; | s.rfind("l"); // 从后往前找 | 类似 find 但方向相反。 |
find_first_of() | 查找字符集中任意字符首次出现 | size_t find_first_of(const string& str, size_t pos = 0) const; | s.find_first_of("aeiou"); // 找元音字母 | 返回匹配的首个字符位置。 |
find_last_of() | 查找字符集中任意字符最后一次出现 | size_t find_last_of(const string& str, size_t pos = npos) const; | s.find_last_of("0123456789"); // 找最后一个数字 | 反向版本。 |
compare() | 比较字符串 | int compare(const string& str) const; | if (s.compare("abc") == 0) { /* 相等 */ } | 返回 0 表示相等,类似 strcmp 。 |
6. 迭代器
名称 | 用途 | 原型 | 代码示例 | 说明 |
---|---|---|---|---|
begin() | 返回指向首字符的迭代器 | iterator begin() noexcept; const_iterator begin() const noexcept; | for (auto it = s.begin(); it != s.end(); ++it) { ... } | 支持正向遍历。 |
end() | 返回尾后迭代器 | iterator end() noexcept; const_iterator end() const noexcept; | for (char c : s) { ... } | 范围循环的基础。 |
rbegin() | 返回反向迭代器(从末尾开始) | reverse_iterator rbegin() noexcept; | for (auto it = s.rbegin(); it != s.rend(); ++it) { ... } | 逆向遍历。 |
7. 数值转换(C++11)
名称 | 用途 | 原型 | 代码示例 | 说明 |
---|---|---|---|---|
stoi() | 字符串转 int | int stoi(const string& str, size_t* pos = 0, int base = 10); | int i = std::stoi("42"); | 失败抛出 invalid_argument 或 out_of_range 。 |
to_string() | 数值转字符串 | string to_string(int val); | std::string s = std::to_string(3.14); | 支持多种数值类型。 |
说明
npos
是std::string
的静态常量,表示无效位置(通常为size_t
最大值)。- C++11 后,
data()
和c_str()
功能相同,均保证以\0
结尾。 - 性能提示:
operator[]
比at()
更快但不安全;reserve()
可减少动态分配次数。
文章目录