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. 最佳实践

  1. 优先使用std::string而非C风格字符串
  2. 预分配空间:对于大量拼接操作,使用reserve()
  3. 使用string_view:对于只读字符串参数
  4. 注意编码:明确指定字符串编码(u8, L, u, U前缀)
  5. 避免不必要的拷贝:使用引用或移动语义
  6. 使用现代C++特性:字面量后缀、格式化等
  7. 检查边界:特别是at()和substr()操作
  8. 考虑异常安全:特别是内存分配可能失败的情况

6. 性能考虑

  1. 小字符串优化(SSO):大多数实现对小字符串(通常≤15字符)有特殊优化
  2. COW(Copy-On-Write):旧版C++标准允许,C++11后禁止
  3. 移动语义:C++11后,string支持移动构造和移动赋值
  4. 内存分配:频繁的字符串操作可能导致多次重新分配

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(); // 3size() 相同。
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()返回底层字符数组(可能无 \0const char* data() const noexcept;const char* p = s.data();C++11 后保证以 \0 结尾(同 c_str())。
c_str()返回 C 风格字符串(带 \0const 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()字符串转 intint stoi(const string& str, size_t* pos = 0, int base = 10);int i = std::stoi("42");失败抛出 invalid_argumentout_of_range
to_string()数值转字符串string to_string(int val);std::string s = std::to_string(3.14);支持多种数值类型。

说明

  1. nposstd::string 的静态常量,表示无效位置(通常为 size_t 最大值)。
  2. C++11 后,data()c_str() 功能相同,均保证以 \0 结尾。
  3. 性能提示operator[]at() 更快但不安全;reserve() 可减少动态分配次数。
文章目录