C++ 字符串
在 C++ 中,字符串主要有两种表现形式:
- C 风格字符串(C‑string):以
'\0'
(null 字符)结尾的字符数组。 std::string
:C++ 标准库提供的、封装了动态长度管理的字符串类。
下面分别介绍它们的特点和用法,并给出常用操作的示例。
1. C 风格字符串
char s1[] = "Hello"; // 编译器自动加上末尾的 '\0',等价于 {'H','e','l','l','o','\0'}
char* s2 = "World"; // 字面量存放在只读区,不能修改内容
- 优点
- 兼容 C 语言接口,例如
printf("%s", s1);
- 控制粒度细,可在底层优化。
- 兼容 C 语言接口,例如
- 缺点
- 必须自己管理长度,容易发生越界或忘记
'\0'
。 - 操作繁琐,常要用到
<cstring>
里的函数,如strlen
、strcpy
、strcat
、strcmp
等:#include <cstring> char dest[20]; strcpy(dest, s1); // 拷贝 strcat(dest, " "); // 拼接 strcat(dest, s2); size_t len = strlen(dest); // 获取长度 (不含 '\0')
- 必须自己管理长度,容易发生越界或忘记
2. std::string
(推荐使用)
std::string
定义在 <string>
头文件中,是对可变长字符序列的封装,使用更安全、简便。
#include <string>
#include <iostream>
using namespace std;
int main() {
string a = "Hello";
string b = "World";
string c = a + " " + b; // 拼接
cout << c << " (长度: " << c.size() << ")\n";
// 访问字符
for (size_t i = 0; i < c.size(); ++i) {
cout << c[i];
}
cout << "\n";
// 查找与替换
auto pos = c.find("World"); // 返回子串在 c 中的起始位置,找不到返回 npos
if (pos != string::npos) {
c.replace(pos, 5, "C++"); // 用 "C++" 替换长度为 5 的子串
}
cout << "替换后: " << c << "\n";
}
常用成员函数
操作 | 函数 | 说明 |
---|---|---|
获取长度 | size() / length() | 返回字符数(不含末尾 \0 ) |
判断是否为空 | empty() | 空则返回 true |
访问字符 | operator[] / at() | at() 有范围检查,越界会抛异常 |
拼接 | operator+ / append() | 返回新字符串或在原串尾部追加 |
比较 | compare() / ==, <, > 等 | 比较字符串大小或字典序 |
查找 | find() , rfind() , find_first_of() | 查找子串或字符集 |
插入 | insert(pos, str) | 在 pos 位置插入字符串 |
替换 | replace(pos, len, str) | 替换从 pos 开始、长度为 len 的子串 |
删除 | erase(pos, len) | 删除从 pos 开始、长度为 len 的字符 |
子串 | substr(pos, len) | 返回从 pos 开始、长度为 len 的新字符串 |
清空 | clear() | 清空内容 |
C 风格访问 | c_str() | 返回以 '\0' 结尾的 const char* |
3. 进阶:std::string_view
C++17 引入了 std::string_view
,它是对已有字符序列的一种轻量只读视图,不负责管理内存,适合高性能场景中避免拷贝:
#include <string_view>
#include <iostream>
using namespace std;
void print_view(string_view sv) {
cout << sv << " (长度: " << sv.size() << ")\n";
}
int main() {
string s = "Hello, C++17!";
print_view(s); // 可以直接传入 std::string
print_view("Literal"); // 也可传入字面量
}
注意:
string_view
不以'\0'
结尾,它只是记录起始地址和长度;函数返回或在视图失效后勿再使用。
4. 宽字符与 Unicode 支持
std::wstring
:宽字符版本,通常每字符占wchar_t
(Windows 上 2 字节,Linux 上 4 字节)。- C++20 提供了
std::u8string
,std::u16string
,std::u32string
,分别对应 UTF‑8、UTF‑16、UTF‑32 编码的字符串。
u8string u8 = u8"你好,世界"; // UTF‑8 编码
u16string u16 = u"Привет"; // UTF‑16 编码
使用这些类型时,要结合对应的转换库(如 <codecvt>
,或使用 ICU、Boost.Locale 等)来进行编码转换和处理。
5. 性能优化建议
- 避免频繁拼接:大量拼接时可预先
reserve()
足够容量,减少多次分配。 - 传参优先使用
const string&
或string_view
:避免不必要的拷贝。 - 小字符串优化(SSO):大多数实现中,
std::string
会在本地缓冲区存储短字符串(通常 ≤15 字节)以减少堆分配。
小结
- 优先使用
std::string
,它安全、易用且功能丰富。 - 对性能有严格要求或仅需读访问时,可考虑
std::string_view
。 - 需要兼容 C 接口或在底层优化时,可使用 C 风格字符串。
- 若处理 Unicode,选用相应的宽字符串或 UTF 专用 string 类型,并配合合适的转换库。
掌握上述用法,便能在大多数 C++ 项目中高效、安全地处理字符串。