跳至内容
Topos Uranos

摘要

文本是程序中最常见的数据:名字、消息、文件的各行、用户的命令。本课按照它在 C++ 中的本来面目来处理它:一个带有自身操作的字符序列。本课介绍 std::string,它像一个专门用于文本的 char 向量那样被创建、拼接、比较和遍历;介绍它的查找与提取操作(find、substr、starts_with、contains)以及值 std::string::npos,find 用它表示什么也没找到;介绍 std::string_view,即观察一段文本而不复制它的视图,以及它所依赖的条件:被观察的文本必须仍然存在。本课最后介绍 C 风格字符串(为了读懂别人的代码),以及 C++ 比较和排序单词的顺序。

学习目标

  1. 使用 std::string 的操作(size、+、find、substr、starts_with、contains)解决文本问题。
  2. 解释 find 的结果并把它与 std::string::npos 比较,预测把它当作普通数字时会发生什么。
  3. 在函数中以 std::string_view 接收文本,并用一个例子说明为什么视图不应比它所观察的字符串活得更久。
  4. 阅读使用 C 风格字符串的代码,并说明它们与 std::string 的区别:空终止符、固定的大小,以及两个字面量相加不能通过编译。

字符串 std::string

创建、拼接、遍历

字符、布尔值、类型转换与常量一课把字符 char 介绍为一个小数字,由一张表把它转换成字母。文本是一串字符;如果是这样,它就可以存放在 std::vector<char> 中,向量一课关于序列所教的内容对它也都成立。然而,人们对文本提出的操作是对任意序列不会提出的:在它后面接上另一段文本,在其中查找一个单词,提取一段,一次性把它写出来。由此需要一个专门的类型,即字符串 std::string,来自头文件 <string>:一个专门用于文本的 char 向量。

C++ 程序
#include <format>#include <print>#include <string>​int main(){    const std::string first{"Ada"};    std::string greeting{"Hola, " + first};    greeting += '.';    std::println("{}({} 个字符)", greeting, greeting.size());    std::println("第一个“{}”,最后一个“{}”", greeting[0], greeting.back());    int spaces{0};    for (const char c : greeting) {        if (c == ' ') {            ++spaces;        }    }    std::println("空格:{}", spaces);    const std::string code{std::format("{}-{:03}", "MAT", 7)};    std::println("{} {}", code, code == "MAT-007");}
输出
Hola, Ada.(10 个字符)第一个“H”,最后一个“.”空格:1MAT-007 true

第 7 行用一个字面量创建字符串。第 8 行把西班牙语问候语“Hola, ”(“你好,”)与它拼接起来:字符串与字面量之间的运算符 + 产生一个新字符串,其中依次包含两者的字符。第 9 行用 += 在末尾添加一个字符,这个运算符也接受字符串或字面量。其余都是关于向量已经知道的内容:size() 统计字符数(10,因为逗号、空格和句点也是字符),greeting[0] 是第一个字符,back() 是最后一个,第 13 到 17 行的循环用循环一课中的范围形式逐个字符地遍历字符串。和向量一样,字符串还支持 push_back、pop_back 和 empty;GCC 15 的库在不开启优化时会在 operator[] 中检查下标(开启优化后,这项检查就消失了)。

最后两行兑现了用函数进行设计一课的预告。std::format 来自 <format>,它接受与 std::println 相同的占位符和相同的格式说明({:03} 用零填充到三位数字),但它不写出结果,而是把结果作为字符串返回:这就是构造一段随后要保存、比较或传给函数的文本的方法。而 == 按内容比较两个字符串:当两者大小相同、字符相同且顺序相同时,它们相等。因此,code == "MAT-007" 为 true,尽管左边的文本是分几部分构造的,右边的则是一个字面量。

比较

除了 == 和 !=,字符串还支持 <、<=、> 和 >=,它们按字典序比较字符串,也就是词典的顺序:同时遍历两个字符串,在第一个不同的位置上,由较小的字符决定;如果一个字符串在没有任何差别的情况下先结束,较短的那个较小。因此,西班牙语单词“casa”小于“cosa”,因为在第二个位置上 'a' < 'o';“mar”小于“marea”,因为前者是后者的前缀。这些运算符比较的是字符串:在两个写在引号中的字面量之间,它们比较的是别的东西,因为字面量不是 std::string,而是一个数组,关于 C 风格字符串的一节会说明这一点。哪个字符“更小”由它的编码决定,而不是由字母表决定;本课最后一节将说明由此得出的结果。