string
上:用 .length() 取字符串长度、用下标访问单个字符、
用循环遍历字符串查找特定字符。
string
上:用 .length() 取字符串长度、用下标访问单个字符、
用循环遍历字符串查找特定字符。
第 4 讲学过 string,当时重点是拼接和
cin / getline 的区别。今天从另一个角度看它——
一个字符串就是一串连续排列的字符,这一点和数组、vector
非常像:都可以用下标访问,都可以用循环遍历。
对这里使用的英文字符串来说,字符串变量.length()
返回这个字符串一共有多少个字符:
#include <iostream>
#include <string>
int main() {
std::string name = "Hello";
std::cout << "长度:" << name.length() << std::endl;
return 0;
} H、e、l、l、o,
正好 5 个字符,空格、标点也都算一个字符。
std::string text = "你好"; 的 text.length()
通常是 6,而不是 2;用下标取到的也是其中一个字节,
不是完整汉字。本节练习先使用英文、数字和英文标点。
字符串变量[下标] 取出对应位置的单个字符——
规则和数组、vector 完全一致:第一个字符下标是 0,
长度为 5 的字符串,最后一个字符下标是 4。
#include <iostream>
#include <string>
int main() {
std::string name = "Hello";
std::cout << name[0] << std::endl; // H
std::cout << name[4] << std::endl; // o
return 0;
} "Hello" 来说,能代表实际字符的下标只有 0~4。
C++17 对字符串有一个特殊规则:只读 name[name.size()]
会得到结尾的空字符 '\0',但它不属于字符串内容;
使用比 size() 更大的下标仍然是未定义行为。
name.at(4) 会得到 'o';
name.at(5) 则会抛出 std::out_of_range。
这和第 16 讲的 vector.at() 是同一套规则。
字符串变量[下标] 不只是用来读取字符的,
也可以直接对某个位置赋值,单独把这一个字符换掉:
#include <iostream>
#include <string>
int main() {
std::string name = "Hello";
name[0] = 'J'; // 直接改掉下标 0 的字符
std::cout << name << std::endl; // Jello
return 0;
} Python:字符串不能这样改
name = "Hello"
name[0] = "J" # 报错!字符串不可变
C++:可以直接改某个字符
std::string name = "Hello";
name[0] = 'J'; // 没问题
string 更像数组、vector——每个位置都能单独改,
这也是为什么把它归到“批量数据”这一类里讲的原因。
把上节课 vector 的遍历写法搬过来,把 .size()
换成 .length() 就行:
#include <cstddef>
#include <iostream>
#include <string>
int main() {
std::string name = "Hello";
for (std::size_t i = 0; i < name.length(); i++) {
std::cout << name[i] << std::endl;
}
return 0;
} for:
for (char c : name) { std::cout << c << std::endl; }。
需要比较左右位置或记录位置时,再使用下标循环。
跑一下下面这段程序,输入一句包含好几个字母 a 的话,
确认统计结果正确:
#include <cstddef>
#include <iostream>
#include <string>
int main() {
std::string text;
std::cout << "请输入一句话:";
std::getline(std::cin, text);
int count = 0;
for (std::size_t i = 0; i < text.length(); i++) {
if (text[i] == 'a') {
count++;
}
}
std::cout << "字母 a 出现了 " << count << " 次。" << std::endl;
return 0;
}
注意 text[i] == 'a' 用的是单引号——
单个字符用单引号,字符串用双引号,第 1 讲提过这个区别。
读入一个字符串(用 cin >> 即可,不含空格),打印它的长度。
读入一个字符串,遍历它,把每个字符单独打印一行。
读入一句话(用 getline,允许包含空格),再读入一个想统计的字符
(char 类型),打印这个字符在句子里出现了几次。
提示:照抄 count_char.cpp 的思路,把写死的
'a' 换成读入的字符变量。
读入一个单词,判断它是不是回文(正着读、反着读都一样,比如
"level")。补全下面的骨架:
std::string word;
std::cin >> word;
bool isPalindrome = true;
std::string::size_type left = 0;
std::string::size_type right = word.length(); // 先指向末尾字符的后一个位置
while (left < right) {
right--; // 先退一步,再访问 word[right]
// 比较 word[left] 和 word[right]
// 不相等就能确定不是回文,可以提前 break
left++;
}
// 根据 isPalindrome 输出“是回文”或“不是回文” left 从头往后走,right 从尾往前走,两人往中间靠拢——
只要中途遇到一对字符不相等,就已经能确定不是回文了,
不用等两人真正相遇。骨架先让 right 指向末尾字符的后一个位置,
每轮先执行 right-- 再访问,可以避免空字符串时计算“长度减 1”发生下溢。
| 能力 | 用到的工具 |
|---|---|
| 取字符串长度 | .length() |
| 访问单个字符 | 字符串[下标],从 0 开始 |
| 遍历整个字符串 | 下标循环,或范围 for |
| 统计 / 查找字符 | 遍历 + if 判断 s[i] |
数组、vector、字符串,这三种“一批数据”的容器,
用的是同一套下标和遍历规则——学会一种,另外两种就顺理成章了。
vector、循环遍历、累加、找最大值全部用进一个完整程序——
用 vector 录入全班成绩,输出总分、平均分、最高分。