在 C++ 中,如果我们想知道一个字符串里保存了多少个字符,或者想检查它是不是空字符串,可以直接调用 std::string 提供的 size()、length() 和 empty() 成员函数。
获取字符串长度:size() 和 length()
对于 std::string 来说,我们既可以调用 size() 函数,也可以调用 length() 函数来获取字符串的长度。这两个函数的作用完全一样,返回的结果也完全相同。
语法:
str.size()
str.length()说明:
既然两个函数完全一样,平时选哪一个更好呢?在实际开发中,我们更推荐统一使用 size()。因为后面学习的 std::vector、std::array 等标准库容器都是使用 size() 获取元素个数的,现在养成习惯就不用再记第二个名字了。
示例 1:获取英文字符串长度
#include <iostream>
#include <string>
int main() {
std::string site = "lvyenet";
std::cout << site.size() << '\n';
std::cout << site.length() << '\n';
return 0;
}运行结果如下。
7
7分析:
"lvyenet" 一共包含 7 个英文字母,所以 site.size() 和 site.length() 返回的结果都是 7。
如果一个字符串里面没有任何字符,也就是空字符串,那么它的长度就是 0。而如果字符串中间包含空格,空格本身也会作为一个字符被统计进去,下面我们来看一个例子。
示例 2:空字符串与空格的长度
#include <iostream>
#include <string>
int main() {
std::string empty1;
std::string empty2 = "";
std::string text = "Hello C++!";
std::cout << empty1.size() << '\n';
std::cout << empty2.size() << '\n';
std::cout << text.size() << '\n';
return 0;
}运行结果如下。
0
0
10分析:
在这个例子中,empty1 和 empty2 里面都没有保存任何字符,所以长度都是 0。而在 "Hello C++!" 中,除了前后的字母和感叹号共 9 个字符外,两个单词中间的空格也算一个字符,因此 text.size() 的结果是 10。
判断空字符串:empty()
从上面的例子可以知道,如果一个字符串是空字符串,它的 size() 就等于 0。除此之外,std::string 还提供了一个专门用来判断字符串是否为空的 empty() 函数。
语法:
str.empty()说明:
其中,empty 在英文中是 “空的” 的意思。如果字符串为空,empty() 返回 true;如果字符串不为空,则返回 false。
示例 3:判断字符串是否为空
#include <iostream>
#include <string>
int main() {
std::string str1 = "";
std::string str2 = "C++";
if (str1.empty()) {
std::cout << "str1 是空字符串" << '\n';
}
if (!str2.empty()) {
std::cout << "str2 不是空字符串" << '\n';
}
return 0;
}运行结果如下。
str1 是空字符串
str2 不是空字符串分析:
str1.empty() 表示判断 str1 是否为空。而在前面加上逻辑非运算符 !,写成 !str2.empty(),就表示判断 str2 “不为空”。如果只是想检查字符串里有没有内容,下面 2 种方式是等价的:
// 方式 1(推荐)
if (str.empty()) {
// 字符串为空
}
// 方式 2
if (str.size() == 0) {
// 字符串为空
}在实际开发中,判断字符串是否为空时,我们更推荐使用方式 1,代码表达的意思更加直观。
结合 size() 访问字符
由于字符串的下标是从 0 开始计算的,一个长度为 5 的字符串,有效下标并不是 1 ~ 5,而是 0 ~ 4。也就是说,只要字符串不是空的,最后一个字符的下标就是 size() - 1。
1. 按下标遍历字符串
如果我们需要使用普通的 for 循环,按从头到尾的顺序结合下标遍历字符串,可以将 i < text.size() 作为循环条件。
这里要留意一个类型细节:因为字符串的长度不可能是负数,所以 size() 返回的类型是 std::size_t。它是 C++ 专门用来表示长度和下标的无符号整数类型,只能保存 0 和正整数。
平时写小练习写成 int i = 0 也能跑通,但定义为 std::size_t i = 0 能与 text.size() 的类型保持一致,避免编译器给出无符号比较警告。如果只是接收长度值,也可以借助 auto 关键字,让编译器自动推导类型。
示例 4:按下标遍历字符串
#include <iostream>
#include <string>
int main() {
std::string text = "Hello";
for (std::size_t i = 0; i < text.size(); i++) {
std::cout << i << ": " << text[i] << '\n';
}
return 0;
}运行结果如下。
0: H
1: e
2: l
3: l
4: o分析:
上面代码中,text.size() 的值是 5,因此循环变量 i 会依次取 0、1、2、3、4。当 i 自增变成 5 时,条件 5 < 5 不成立,循环就会立即退出,刚好遍历完所有字符而不会发生越界。
2. 获取最后一个字符
如果想要直接获取字符串的最后一个字符,除了使用下标 text[text.size() - 1] 之外,std::string 还提供了更直观的 back() 函数。不过无论是哪种方式,在获取之前都应该先确保字符串不是空的。
示例 5:获取最后一个字符
#include <iostream>
#include <string>
int main() {
std::string text = "Hello";
if (!text.empty()) {
std::cout << text[text.size() - 1] << '\n';
std::cout << text.back() << '\n';
}
return 0;
}运行结果如下。
o
o分析:
在这个例子中,我们先通过 !text.empty() 确认字符串不为空,再使用 text[text.size() - 1] 或 text.back() 取出末尾字符 'o'。同理,获取首字符可以使用 text[0] 或 text.front()。
注意 使用 text.size() - 1 或 text.back() 前必须确保字符串非空。因为 size() 返回无符号整数,对空字符串算 0 - 1 会回绕成极大正整数,引发越界错误。
中文字符串的字节长度
对于英文字母、数字和英文标点来说,size() 返回的数值正好等于我们在屏幕上看到的字符个数。但如果字符串里包含了中文汉字,结果就不一样了。
示例 6:计算中文字符串的长度
#include <iostream>
#include <string>
int main() {
std::string site = "绿叶网";
std::cout << site.size() << '\n';
return 0;
}运行结果如下。
9分析:
“绿叶网” 明明只有 3 个汉字,为什么 site.size() 输出的结果却是 9 呢?这是因为 std::string 的 size() 和 length() 统计的其实是字符串底层占用的 “字节数”。
一个英文字符恰好占用 1 个字节,而在常用的 UTF-8 编码下,一个中文汉字通常占用 3 个字节,因此 3 个汉字算出来的总字节数就是 9。
正因如此,对于包含中文的字符串,小伙伴们不要使用 site[0] 这样的方式去单独取某一个汉字。因为 site[0] 只能取出第一个汉字的其中一个字节,直接打印出来会变成乱码。
常见问题
1. 获取字符串长度时,size()、length() 和 sizeof 有什么区别?
这三者的计算对象完全不同,平时写代码时要注意区分:
size() 与 length():两者完全等价,用于获取 std::string 中实际保存的字符字节数,日常开发推荐统一使用 size()。
sizeof(string 变量):求字符串长度不能使用 sizeof 运算符。它算出来的只是 std::string 对象本身固定的管理结构大小,不管里面存了多少字符,结果都不会变。
sizeof(char 数组):对于 C 风格字符串,比如 char word[] = "lvye";,sizeof 会把末尾隐藏的 '\0' 也算进去,求有效字符数应该使用 std::strlen()。
