C++ 字符串
在 C++ 中使用字符串
Section titled “在 C++ 中使用字符串”C++ 提供了两种主要的方式来处理字符序列(字符串):
std::string:标准的 C++ 字符串类(在<string>头文件中)。这是在 C++ 中处理字符串的首选、现代、更安全、更方便的方式。- C 风格字符字符串:继承自 C 语言的以空字符 (
\0) 结尾的字符数组(char*或char[])。虽然在与 C API 或旧代码交互时必不可少,但在现代 C++ 中直接操作它容易出错,通常不推荐使用。
std::string 类(推荐)
Section titled “std::string 类(推荐)”定义在 <string> 头文件中的 std::string 类提供了强大且功能丰富的字符串管理方式。它自动处理内存管理,避免了许多与 C 风格字符串相关的常见错误。
主要特性和操作:
Section titled “主要特性和操作:”- 初始化:可以从字符串字面量、其他
std::string对象或字符串的一部分进行初始化。 - 赋值:使用
=运算符。 - 连接:使用
+或+=运算符。 - 比较:使用标准比较运算符(
==,!=,<,>,<=,>=)。 - 长度/大小:使用
.length()或.size()方法(它们是等价的)。 - 访问字符:使用
[]运算符(不进行边界检查)或.at()方法(如果索引无效则抛出std::out_of_range异常)。 - 追加:使用
.append()或+=。 - 查找子字符串:使用
.find()、.rfind()、.find_first_of()、.find_last_of()等方法。 - 子字符串:使用
.substr()提取字符串的一部分。 - 修改:使用
.insert()、.erase()、.replace()等方法。 - 转换为 C 风格:使用
.c_str()获取一个const char*指针(以空字符结尾),用于与 C 函数兼容。
使用 std::string 的示例:
#include <iostream>#include <string>#include <stdexcept> // 用于 std::out_of_range
int main() { // 初始化 std::string greeting = "Hello"; std::string name("World"); // 构造函数初始化 std::string message; // 空字符串
// 连接和赋值 message = greeting + ", " + name + "!"; // 使用 + greeting += ", again!"; // 使用 +=
std::cout << "Greeting: " << greeting << std::endl; std::cout << "Message: " << message << std::endl;
// 长度 std::cout << "Message length: " << message.length() << std::endl;
// 访问字符 std::cout << "First character: " << message[0] << std::endl; // 'H' try { std::cout << "Character at index 6: " << message.at(6) << std::endl; // 'W' // 这会抛出 std::out_of_range 异常 // std::cout << message.at(100); } catch (const std::out_of_range& oor) { std::cerr << "Out of Range error: " << oor.what() << std::endl; }
// 查找子字符串 size_t commaPos = message.find(','); if (commaPos != std::string::npos) { // npos 表示“未找到” std::cout << "Comma found at index: " << commaPos << std::endl; }
// 子字符串 std::string sub = message.substr(7, 5); // 从索引 7 开始提取 5 个字符 std::cout << "Substring (name): " << sub << std::endl; // "World"
// 转换为 C 风格字符串(用于 C API) printf("Message as C-string: %s\n", message.c_str());
return 0;}预期输出:
Greeting: Hello, again!Message: Hello, World!Message length: 13First character: HCharacter at index 6: WComma found at index: 5Substring (name): WorldMessage as C-string: Hello, World!C 风格字符字符串(传统方式)
Section titled “C 风格字符字符串(传统方式)”C 风格字符串本质上是以特殊空字符 (\0) 结尾的字符数组。它们使用指针 (char*) 管理,如果动态分配则需要手动管理内存。
声明和初始化:
// 从字符串字面量初始化(大小自动包含空终止符)char greeting[] = "Hello"; // 大小是 6 ('H','e','l','l','o','\0')
// 显式指定大小的数组(必须足够大以容纳字符串 + 空终止符)char farewell[10] = "Bye"; // {'B','y','e','\0', ?, ?, ?, ?, ?, ?}
// 指针初始化(指向只读内存中的字面量 - 修改是未定义行为!)const char* readOnlyStr = "Constant String";greeting[] 的内存表示:
Index: 0 1 2 3 4 5Value: 'H' 'e' 'l' 'l' 'o' '\0'常用 C 字符串函数(<cstring> 头文件):
Section titled “常用 C 字符串函数(<cstring> 头文件):”<cstring> 头文件(在 C 中是 <string.h>) 提供了用于操作 C 风格字符串的函数。警告: 这些函数中的许多是不安全的,因为它们不执行边界检查,如果目标缓冲区太小,可能导致缓冲区溢出。
| 序号 | 函数 | 用途和安全注意事项 |
|---|---|---|
| 1 | strcpy(dest, src) | 将字符串 src 复制到 dest。不安全! 可能导致 dest 溢出。 |
| 2 | strncpy(dest, src, n) | 最多复制 n 个字符。更安全,但如果达到 n 个字符,可能不会以空字符结尾。 |
| 3 | strcat(dest, src) | 将字符串 src 追加到 dest。不安全! 可能导致 dest 溢出。 |
| 4 | strncat(dest, src, n) | 最多从 src 追加 n 个字符。更安全,总是以空字符结尾。 |
| 5 | strlen(str) | 返回 str 的长度(不包括空终止符)。 |
| 6 | strcmp(s1, s2) | 比较 s1 和 s2。如果相等返回 0,如果 s1<s2 返回 <0,如果 s1>s2 返回 >0。 |
| 7 | strncmp(s1, s2, n) | 最多比较 n 个字符。 |
| 8 | strchr(str, ch) | 返回字符 ch 在 str 中第一次出现的指针,如果未找到则返回 nullptr。 |
| 9 | strstr(haystack, needle) | 返回字符串 needle 在 haystack 中第一次出现的指针,如果未找到则返回 nullptr。 |
使用 C 风格字符串的示例(仅作演示):
#include <iostream>#include <cstring> // 在 C++ 中使用 <cstring>
int main() { char str1[20] = "Hello"; // 确保缓冲区足够大! char str2[] = "World"; char str3[20]; int len;
// 将 str1 复制到 str3(使用更安全的 strncpy 或可用的 strcpy_s) // strcpy(str3, str1); // 不安全版本 strncpy(str3, str1, sizeof(str3) - 1); // 更安全的复制 str3[sizeof(str3) - 1] = '\0'; // 确保以空字符结尾 std::cout << "strncpy(str3, str1, ...): " << str3 << std::endl;
// 将 str2 连接到 str1(使用更安全的 strncat 或 strcat_s) // strcat(str1, str2); // 不安全版本 strncat(str1, " ", sizeof(str1) - strlen(str1) - 1); strncat(str1, str2, sizeof(str1) - strlen(str1) - 1); // 更安全的连接 std::cout << "strncat(str1, ...): " << str1 << std::endl;
// 连接后 str1 的总长度 len = strlen(str1); std::cout << "strlen(str1): " << len << std::endl;
// 比较 if (strcmp(str2, "World") == 0) { std::cout << "str2 is equal to \"World\"" << std::endl; }
return 0;}预期输出:
strncpy(str3, str1, ...): Hellostrncat(str1, ...): Hello Worldstrlen(str1): 11str2 is equal to "World"总结: 虽然理解 C 风格字符串对于 C++ 开发者很重要(尤其是在处理 C 库或低级代码时),但在现代 C++ 中进行通用字符串操作时,始终首选 std::string。它更安全、更易于使用,并且不易出错。
std::string_view (C++17)
Section titled “std::string_view (C++17)”在需要只读访问字符串数据而无需创建新的 std::string(例如,作为函数参数)的情况下,可以考虑使用 std::string_view(来自 <string_view> 头文件)。它提供了一个对现有字符序列(可以是 std::string、C 风格字符串或字符串字面量)的非拥有视图。