适用对象: 已完成第 4 课(数组)的同学 使用方式: 自学讲义。本课延续第 2 课起的厚度——4 个知识模块 + 8 题练习 + 加餐,做完大约需要 1.5 小时 学完本课,你应该能:
string 存一段文字,会取长度、拼接、按下标取字符cin >> s(读一个词)和
getline(cin, s)(读一整行)char 类型和单引号,会用
s[i] - '0'、c - 'a' + 'A' 这类字符运算先把上节课的"读入-处理-输出"三部曲跑一遍热热身——读入 n 个成绩,统计及格(≥60)的人数:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
int a[1005];
int cnt = 0;
for (int i = 0; i < n; i++) {
cin >> a[i];
if (a[i] >= 60) cnt++;
}
cout << cnt << endl;
return 0;
}输入:
5
78 45 92 60 30
输出:
3
上节课加餐里我提过一句:"字符串其实也是字符的数组,s[i]
能取出单个字符。" 今天就把这句话彻底展开。
Python 你这样写:
s = "hello"
t = s + " world" # 拼接
print(len(s)) # 5
print(t) # hello world
print(s[0], s[4]) # h oC++ 改成这样:
#include <bits/stdc++.h>
using namespace std;
int main() {
string s = "hello";
string t = s + " world"; // 拼接,和 Python 一样用 +
cout << s.length() << endl; // 5
cout << t << endl; // hello world
cout << s[0] << " " << s[4] << endl; // h o
return 0;
}输出:
5
hello world
h o
对照下来只有一处真正不同:求长度。
| Python | C++ |
|---|---|
len(s) |
s.length() 或
s.size()(两个完全一样,随便用哪个) |
📝 注意这个反差:上节课说过 C++ 数组没有
len,必须自己另开变量 n 记账;但
string
是有长度的——它比数组"聪明",自己记得住装了多少个字符。所以处理字符串时不需要额外传一个长度变量。
遍历字符串——就是上节课的数组循环,把 a[i] 换成
s[i]:
#include <bits/stdc++.h>
using namespace std;
int main() {
string s = "abc";
for (int i = 0; i < s.length(); i++) {
cout << s[i] << " ";
}
cout << endl;
return 0;
}输出:a b c
📝 有的编译器会对 i < s.length() 提示一句
comparison between signed and unsigned integer expressions
警告——这只是警告不是错误,程序照跑不误,原因以后学,现在直接忽略。
新建 check1.cpp:定义
string s = "python";,输出它的长度,再用循环把每个字符倒着输出(提示:从
s.length() - 1 倒数到
0,就是上节课的数组逆序)。
cin >> s 和 getline 的区别(15
分钟)Python 的 input() 读一整行,怎么读都不会出错。C++
这里有个Python 学生百分之百会踩的坑:
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
cout << s << endl;
return 0;
}输入 hello world,输出却只有:
hello
⚠️ cin >> s
遇到空格就停下来,只能读到一个"单词"。这和
cin >> n 读整数是同一套规则——空格是分隔符。
要读一整行(包含空格),必须用
getline:
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
getline(cin, s); // 读一整行,直到回车
cout << s << endl;
return 0;
}输入 hello world,输出:
hello world
| Python | C++ | 读到什么 |
|---|---|---|
input() |
getline(cin, s); |
一整行(含空格) |
input().split()[0] |
cin >> s; |
一个词,遇空格停 |
⚠️ 混用大坑:cin >> n 之后紧接着
getline,会读到一个空行。 因为
cin >> n
读完数字后,把行末的那个回车留在了输入里,getline
一上来就撞上它,以为这一行读完了。解决办法是中间加一句
cin.ignore();:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
cin.ignore(); // 把 cin >> n 剩下的那个回车扔掉
string s;
getline(cin, s);
cout << n << " " << s << endl;
return 0;
}输入:
3
hello world
输出:
3 hello world
📝 先照抄 cin.ignore();
这一行,记住"cin >> 后面要接
getline,中间加一句
ignore"这条规矩就够了,它的完整原理以后学。
这是Python 里根本没有的概念。Python 中
"a" 和 'a' 完全一样,都是长度为 1
的字符串;C++ 里这两个是两种不同的类型:
| 写法 | 类型 | 含义 |
|---|---|---|
'a' 单引号 |
char |
一个字符 |
"a" 双引号 |
string |
一个字符串(哪怕只有一个字符) |
s[i] 取出来的东西,类型就是 char。
关键认知:char 本质上就是一个数字。
每个字符都有一个编号(叫 ASCII 码),'a' 是
97,'b' 是 98,'A' 是 65,'0' 是
48……正因为是数字,字符才能加减和比大小:
#include <bits/stdc++.h>
using namespace std;
int main() {
char c = 'a';
cout << c << endl; // a
cout << (int)c << endl; // 97 —— 强行当数字看,就是它的编号
char d = c - 'a' + 'A'; // 小写转大写
cout << d << endl; // A
char num = '7';
cout << num - '0' << endl; // 7 —— 数字字符转成真正的数字
return 0;
}输出:
a
97
A
7
两个必须背下来的套路:
| 目的 | 写法 | 原理 |
|---|---|---|
| 数字字符 → 数值 | s[i] - '0' |
'7' 是 55,'0' 是 48,相减得 7 |
| 小写 → 大写 | c - 'a' + 'A' |
先算出它是第几个字母,再从 'A' 数过去 |
三个必须背下来的判断:
c >= 'a' && c <= 'z' // 是小写字母
c >= 'A' && c <= 'Z' // 是大写字母
c >= '0' && c <= '9' // 是数字字符
⚠️ c >= '0' && c <= '9'
判断的是"字符 '7'",不是"数字 7"。 别把
s[i] == 7 和 s[i] == '7' 搞混了——前者在比编号
7(那是个看不见的控制字符),后者才是你想要的。
把上节课的"计数套路"搬到字符串上——统计元音字母的个数:
#include <bits/stdc++.h>
using namespace std;
int main() {
string s = "programming";
int cnt = 0;
for (int i = 0; i < s.length(); i++) {
char c = s[i];
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') cnt++;
}
cout << cnt << endl;
return 0;
}输出:3(o、a、i
三个)
📝
看出来了吗?这就是上节课数组计数套路的原样搬运——只是把
a[i] 换成了
s[i],把"数偶数"换成了"数元音"。学过的东西没白学。
错误 1:单引号双引号用混了
char c = "a"; ← 双引号是字符串,不能塞进 char 里
编译器报错大致长这样:
error: invalid conversion from 'const char*' to 'char'
看到 invalid conversion(无效的类型转换)+
char*
这些词,第一反应就是去检查引号。反过来
string s = 'a'; 也是错的。
📝 char 配单引号,string
配双引号,一一对应,不要混。
错误 2:cin >> s
读不到空格后面的内容
string s;
cin >> s; // 输入 hello world
cout << s << endl; // 只输出 hello,world 不见了
不是程序崩了,是 cin >>
本来就只读一个词。要整行就换
getline(cin, s)。
错误 3:字符串下标越界
string s = "abc";
cout << s[3] << endl; ← s 只有下标 0、1、2
和数组越界一模一样:s 的有效下标是 0 到
s.length() - 1。循环条件写
i < s.length(),千万别写成
i <= s.length()。
📝 字符串的下标规则和数组完全一样,上节课那两个坑在这里原样成立。
⭐ 基础题 1:字符串长度(文件名
t1.cpp)
输入一个不含空格的字符串,输出它的长度。
输入样例:
hello
输出样例:
5
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
cout << s.length() << endl;
return 0;
}⭐ 基础题 2:逐字符输出(文件名
t2.cpp)
输入一个不含空格的字符串,把每个字符单独输出一行。
输入样例:
abc
输出样例:
a
b
c
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
for (int i = 0; i < s.length(); i++) {
cout << s[i] << endl;
}
return 0;
}⭐⭐ 实战题 3:统计元音(文件名
t3.cpp)
输入一个不含空格的小写字符串,输出其中元音字母(a e i o u)的个数。
输入样例:
programming
输出样例:
3
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
int cnt = 0;
for (int i = 0; i < s.length(); i++) {
char c = s[i];
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') cnt++;
}
cout << cnt << endl;
return 0;
}⭐⭐ 实战题 4:全部转大写(文件名
t4.cpp)
输入一整行文字(可能含空格和标点),把其中的小写字母全部转成大写,其余字符原样保留,输出结果。
输入样例:
Hello, World!
输出样例:
HELLO, WORLD!
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
getline(cin, s);
for (int i = 0; i < s.length(); i++) {
if (s[i] >= 'a' && s[i] <= 'z') {
s[i] = s[i] - 'a' + 'A'; // 直接改回原字符串
}
}
cout << s << endl;
return 0;
}因为有空格和标点,必须用 getline。注意
s[i] = ...
——字符串的下标和数组一样,既能读也能写。
⭐⭐ 实战题 5:字符分类统计(文件名
t5.cpp)
输入一整行文字,统计其中字母、数字、其他字符各有多少个,用空格分隔输出三个数。
输入样例:
abc 123 !!
输出样例:
3 3 4
(3 个字母、3 个数字,其余 2 个空格 + 2 个感叹号共 4 个。)
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
getline(cin, s);
int letter = 0, digit = 0, other = 0;
for (int i = 0; i < s.length(); i++) {
char c = s[i];
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) letter++;
else if (c >= '0' && c <= '9') digit++;
else other++;
}
cout << letter << " " << digit << " " << other << endl;
return 0;
}三个计数变量各管一类,if / else if / else
保证每个字符只被数一次——这是第 2 课的 else if
链在字符串上的应用。
⭐⭐⭐ 冲刺题 6:回文判断(文件名
t6.cpp)
输入一个不含空格的字符串,判断它是不是回文(正着读和倒着读一样)。是输出
是回文,不是输出 不是回文。
输入样例1: 输入样例2:
level hello
输出样例1: 输出样例2:
是回文 不是回文
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
int len = s.length();
bool ok = true;
for (int i = 0; i < len / 2; i++) {
if (s[i] != s[len - 1 - i]) ok = false; // 第 i 个 和 倒数第 i 个 比
}
cout << (ok ? "是回文" : "不是回文") << endl;
return 0;
}s[i] 和 s[len-1-i]
是"对称的一对":i=0 配最后一个,i=1
配倒数第二个……只需要比到中间(i < len / 2)就够了,再往后就是重复比一遍。
⭐⭐⭐ 冲刺题 7:各位数字之和(文件名
t7.cpp)
输入一个只由数字字符组成的字符串(可能很长,比 int
装得下的还长),求各位数字之和。
输入样例:
12345
输出样例:
15
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
int sum = 0;
for (int i = 0; i < s.length(); i++) {
sum += s[i] - '0'; // 数字字符 → 数值
}
cout << sum << endl;
return 0;
}📝 这题演示了字符串的一个重要用途:装一个超大的数。
int 最多装到 21 亿左右,但用字符串可以装 100
位甚至更长的数字,再一位一位处理。高精度计算就是这么做的,以后学。
⭐⭐⭐ 冲刺题 8:查找字符(文件名
t8.cpp,选做)
第一行输入一个不含空格的字符串,第二行输入一个字符。输出该字符出现的次数,第二行输出它出现的所有下标(从 0 开始,空格分隔)。保证至少出现一次。
输入样例:
banana
a
输出样例:
3
1 3 5
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
char c;
cin >> s >> c;
int cnt = 0;
for (int i = 0; i < s.length(); i++) {
if (s[i] == c) cnt++;
}
cout << cnt << endl;
for (int i = 0; i < s.length(); i++) {
if (s[i] == c) cout << i << " ";
}
cout << endl;
return 0;
}cin >> c 读一个 char,正好和
s[i] 类型一致,可以直接用 == 比较。C++
其实有个现成的 s.find()
函数能找位置,以后学——现在自己用循环写一遍,才明白它在干什么。
取子串 s.substr(起始下标, 取几个):
#include <bits/stdc++.h>
using namespace std;
int main() {
string s = "programming";
cout << s.substr(0, 3) << endl; // pro
cout << s.substr(3, 4) << endl; // gram
string a = "apple", b = "banana";
if (a < b) cout << a << " 排在 " << b << " 前面" << endl;
return 0;
}输出:
pro
gram
apple 排在 banana 前面
s.substr(3, 4) 表示"从下标 3 开始,取 4 个字符",对应
Python 的 s[3:7]<、>、==
比较,比的是字典序(就是词典里的先后顺序),这点和
Python 一样s.find()(查子串位置)、reverse()(翻转)、stoi
/
to_string(字符串和数字互转)、二维数组、vector
一律以后学。
| Python | C++ | 备注 |
|---|---|---|
s = "hello" |
string s = "hello"; |
双引号 |
len(s) |
s.length() 或 s.size() |
数组没有,但 string 有 |
s + t |
s + t |
拼接写法一样 |
s[i] |
s[i] |
下标从 0 开始,可读可写 |
input() |
getline(cin, s); |
读一整行(含空格) |
| (无对应) | cin >> s; |
只读一个词,遇空格停 |
'a' 和 "a" 一样 |
'a' 是 char,"a" 是
string |
C++ 独有的区分 |
ord(c) |
(int)c |
字符本来就是数字,直接减也行 |
s[3:7] |
s.substr(3, 4) |
参数是"起点 + 个数",不是"起点 + 终点" |
字符运算三件套:
s[i] - '0' // 数字字符 → 数值
c - 'a' + 'A' // 小写 → 大写
c >= '0' && c <= '9' // 判断是不是数字字符
三个字符串专属坑:
| 症状 | 原因 |
|---|---|
invalid conversion from 'const char*' |
char 用了双引号 |
输入 hello world 只读到 hello |
该用 getline 却用了 cin >> s |
cin >> n 后 getline 读到空行 |
中间少了 cin.ignore(); |
本课新符号:
string、char、'单引号'、s.length()、getline(cin, s)、cin.ignore()、(int)c、s.substr(pos, len)
cin >> s 和
getline(cin, s) 的区别,各举一个该用哪个的场景'a' 和 "a" 在 C++
里为什么是两个不同的东西?Python 里为什么没这个麻烦?