Menu

C++ 字符串教程:std::string 基础、方法与陷阱

如何在 C++ 中使用 std::string:安全地创建、拼接、查找和截取文本,以及为什么正经开发中你几乎永远不想用裸 char*。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

std::string:会自我管理的文本

在上一页中,你看到了智能指针如何让一个对象拥有资源并自动清理。std::string 就是把同样的思路用在文本上:它拥有一块字符缓冲区,追加内容时让它增长,字符串离开作用域时释放它。你永远不用调用 new,不用数字节数,也不用担心漏掉空终止符。

使用它需要包含 <string>。这个类位于 std 命名空间中。

那个 + 运算符可是在干实事:它分配一块足够容纳两部分的新缓冲区,再把它们拷贝进去。如果用裸 char*,也就是一个指向字符缓冲区的普通指针,你就得动用 strcpy 和 strcat,还得祈祷缓冲区足够大。std::string 让这一整类错误都消失了。

创建和拼接字符串

你可以用 + 拼接,但原地增长字符串的主力是 +=。它追加到已有的缓冲区上,而不是每次都生成一个全新的字符串,这在循环里很重要。

一个常见的意外:+ 需要至少有一个操作数已经是 std::string。两个字符串字面量只是 const char*,所以 "a" + "b" 无法编译:两个裸指针之间没有 operator+。先把其中一个变成 string:

string s = "a" + "b";              // error: can't add two const char*
string s = string("a") + "b";      // fine - left side is a std::string
string s = "a"s + "b";             // fine in C++14+, the "s" literal suffix

注意最后一种写法用的是 <string> 中的 s 后缀(using namespace std::string_literals;),它直接把字面量变成 std::string。

访问字符串内部

std::string 的行为就像一个 char 容器,所以你可以按下标访问它、遍历它,也可以取出其中的片段。

substr(pos, len) 返回一个从原字符串拷贝出来的全新字符串,不会修改原字符串。注意边界:对一个 5 个字符的字符串访问 word[10] 是未定义行为,它不会抛出异常,只会读到垃圾值。如果你想要会抛出 std::out_of_range 的带检查访问,就用 word.at(10) 代替 word[10]。

另一个经典陷阱:.size() 返回的是无符号类型(size_t)。像 for (size_t i = word.size() - 1; i >= 0; --i) 这样写倒序循环永远不会结束,因为无符号的 i 永远不会小于零,它会回绕成一个巨大的数。需要倒序遍历字符串时,用有符号的下标,或者调整循环条件。

查找和替换

find 定位一个子串或字符,返回它的起始下标。找不到目标时,它返回特殊常量 std::string::npos:始终和它比较,而不要以为 find 会返回 -1。

要原地修改文本,replace(pos, len, text) 把一段内容换成新内容(长度可以不同),insert/erase 则用来添加或删除片段:

字符串与数字

文本和数字不会自己互相转换:"42" 是三个字符,而不是整数 42。标准库提供了双向的转换函数。用 stoi、stod 等把文本解析成数字,用 to_string 反向转换。

这里有两个陷阱。第一,stoi("abc") 会抛出 std::invalid_argument,所以要用 try/catch 块保护不可信的输入。第二,to_string(3.99) 给出的是完整的 3.990000:如果你需要控制精度和格式,那是字符串流的活儿,也正是我们接下来要去的地方。

try {
    int n = std::stoi(userInput);
} catch (const std::invalid_argument&) {
    std::cout << "That wasn't a number.\n";
}

下一节:输入与输出

你一直在用 cout 打印字符串,但从用户那里把它们读回来另有一些意外:cin >> name 遇到第一个空格就停下,所以像“Ada Lovelace”这样的一整行需要改用 std::getline。下一页会好好讲解 C++ 的输入与输出:流运算符,读取整行,以及如何混用 >> 和 getline 而不被残留的换行符绊倒。

常见问题

C++ 中 std::string 和 char* 有什么区别?

std::string 是一个类,它拥有并管理自己的内存,按需增长,并自动清理。char* 只是一个指向字符的裸指针,字符以 '\0' 结尾:缓冲区、长度和生命周期都得你自己管理。几乎所有场合都优先用 std::string,它能消除整类的缓冲区溢出和悬空指针 bug。

C++ 中如何获取字符串的长度?

对 std::string 调用 .size() 或它的别名 .length():name.size() 以 size_t 返回字符个数。两者返回的值相同;size() 更常见,因为它和其他所有 STL 容器保持一致。

C++ 中如何把字符串转换成数字?

int 用 std::stoi,double 用 std::stod,还有类似的 stol、stof。例如:int n = std::stoi("42");。如果文本不是数字,它们会抛出 std::invalid_argument,所以当输入不可信时,要用 try/catch 把它们包起来。

Coddy 编程语言插图

和 Coddy 一起学编程

开始