调整课次结构:前9节重新排序,新建第06课一维数组,后3节不变

This commit is contained in:
chengzi
2026-07-03 15:27:11 +08:00
parent 30f803784a
commit b973fb7825
29 changed files with 1301 additions and 13 deletions

View File

@@ -0,0 +1,301 @@
# 第8课字符数组与string
## 教学目标
- 掌握 string 类的基本操作(长度、访问、截取、查找)
- 理解字符数组与 string 的区别
- 能处理字符串遍历、逆序、回文判断等常见问题
- 会用 `getline` 读取含空格的字符串
## 核心考点:字符串输入和逐字符处理
### 考什么
常见题型包括读取一句话、统计各类字符、查找或替换子串、反转字符串和判断回文。首先要正确读入完整字符串,然后通过下标逐个处理字符。
### 必须理解
`cin >> s` 遇到空格就停止,只适合读取一个不含空格的单词;`getline(cin, s)` 会读取整行。如果前面刚用 `cin >>` 读取数字,输入缓冲区可能残留换行符,应先用 `cin.ignore()` 清掉,否则 `getline` 会读到空行。字符串长度为 `s.length()`,合法下标仍是 `0~length-1`。逐字符处理时,每个 `s[i]` 都是 `char`,可配合 `isdigit``isalpha` 等函数分类。
### 核心写法
```cpp
int n;
cin >> n;
cin.ignore();
string s;
getline(cin, s);
for (int i = 0; i < s.length(); i++) {
char c = s[i];
// 对 c 进行判断、统计或修改
}
```
输入方式由数据格式决定;遍历上界由字符串的实际长度决定。
### 常见错误
-`cin >> s` 读取句子,只得到第一个单词。
- 混用 `cin``getline` 时忘记处理残留换行。
- 写成 `i <= s.length()`,访问字符串末尾之外的位置。
- 把字符下标与子串位置混淆,或未检查 `find` 是否找到。
### 判断是否掌握
能根据输入是否含空格选择 `cin``getline`,能解释空行问题,并能无越界地遍历字符串完成字符统计或修改。
---
## 题目一:字符串输入输出与字符统计
### 核心代码(一句话)
> 字符串处理的核心:**`getline(cin, s)` 读整行****`s.length()` 取长度****`s[i]` 访问每个字符**,配合字符判断函数做统计。
### 自然语言思路
输入一句话,统计里面的大写字母、小写字母、数字、其他字符各有几个。
第一步,用 `getline(cin, s)` 读入整行(不能用 `cin >> s`,因为碰到空格就停了)。
第二步,用 `s.length()``s.size()` 拿到字符串长度。
第三步,用 `for` 循环遍历每个字符 `s[i]`
第四步,对每个字符用判断函数分类:`isupper()` 判断大写、`islower()` 判断小写、`isdigit()` 判断数字,剩下的就是"其他"。
第五步,四个计数器分别累加,最后输出。
> 提示:`isupper`、`islower`、`isdigit` 这些函数在第1课学过头文件是 `<cctype>`。
### 伪代码
```
1. 输入一行字符串 s用 getline
2. upper = 0, lower = 0, digit = 0, other = 0
3. for i = 0 到 s.length() - 1
4. if isupper(s[i]) upper++
5. else if islower(s[i]) lower++
6. else if isdigit(s[i]) digit++
7. else other++
8. 输出 upper, lower, digit, other
```
### 真实代码
```cpp
#include <iostream>
#include <string>
#include <cctype> // isupper, islower, isdigit
using namespace std;
int main() {
string s;
getline(cin, s); // 读整行,含空格
int upper = 0, lower = 0, digit = 0, other = 0;
for (int i = 0; i < s.length(); i++) {
if (isupper(s[i])) {
upper++;
} else if (islower(s[i])) {
lower++;
} else if (isdigit(s[i])) {
digit++;
} else {
other++;
}
}
cout << "大写:" << upper
<< ",小写:" << lower
<< ",数字:" << digit
<< ",其他:" << other << endl;
return 0;
}
```
### 注意点
1. **`cin >> s` vs `getline(cin, s)`**`cin` 遇空格换行就停,`getline` 读整行(含空格)。如果前面用过 `cin`,需要用 `cin.ignore()` 吃掉残留的换行符。
2. **`s.length()` vs `s.size()`**:两个完全一样,都可以用,没有区别。
3. **访问越界**:下标范围 `0``s.length() - 1`,访问 `s[s.length()]` 会越界。
4. **空格算"其他"**:上面的代码中空格既不是大写/小写/数字,会归入 other。如果想单独统计空格加一个 `else if (s[i] == ' ')`
---
## 题目二:字符串查找与替换
### 核心代码(一句话)
> 查找用 **`s.find("目标")`**,找不到返回 **`string::npos`**;替换用 **`s.replace(起始位置, 长度, "新内容")`**;截取用 **`s.substr(起始位置, 长度)`**。
### 自然语言思路
在一句话里找到某个词的位置,然后把它替换成另一个词。
第一步,用 `s.find("目标词")` 查找,它会返回目标词第一次出现的位置(下标)。
第二步,如果找到了(返回值 `!= string::npos`),用 `s.replace(位置, 长度, "新词")` 替换。
第三步,如果找不到,`find` 返回 `string::npos`(一个很大的特殊值),直接提示"没找到"。
第四步,`substr` 用来截取子串,比如 `s.substr(0, 5)` 表示从下标 0 开始取 5 个字符。
### 伪代码
```
1. 输入字符串 s
2. 输入要查找的词 target
3. pos = s.find(target)
4. if pos != string::npos
5. 输入替换词 replacement
6. s.replace(pos, target.length(), replacement)
7. 输出 s
8. else
9. 输出 "未找到"
```
### 真实代码
```cpp
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
getline(cin, s);
string target;
cin >> target;
int pos = s.find(target);
if (pos != string::npos) {
string replacement;
cin >> replacement;
s.replace(pos, target.length(), replacement);
cout << s << endl;
} else {
cout << "未找到" << endl;
}
return 0;
}
```
### 注意点
1. **`string::npos` 是什么**`npos` = "no position",是一个特殊常量,表示"没找到"。不要用 `-1``0` 来判断。
2. **`replace` 会修改原字符串**`s.replace(...)` 直接改了 `s`,如果还需要原来的值,先备份。
3. **`substr` 参数**`s.substr(开始下标, 长度)`。如果省略第二个参数,默认截取到末尾。
4. **find 只找第一次出现**:如果想找所有出现的位置,需要循环 + 更新起始位置。
---
## 题目三:字符串逆序与回文判断
### 核心代码(一句话)
> 字符串逆序的核心:**从最后一个字符 `s.length()-1` 遍历到第一个字符 `0`**;回文判断的核心:**首尾字符两两比较,`s[i] != s[n-1-i]` 就不是回文**。
### 自然语言思路
**字符串逆序输出**
第一步,拿到最后一个字符的下标:`s.length() - 1`
第二步,从后往前遍历:`for (i = s.length()-1; i >= 0; i--)`
第三步,逐个输出 `s[i]`,就得到了逆序字符串。
**判断回文**
第一步,回文就是正着读和反着读一样,比如 "abcba"、"level"。
第二步,只需要比较前一半和后一半:`s[0]``s[n-1]``s[1]``s[n-2]`……直到中间。
第三步,用 `bool` 变量记录结果,默认 `true`。一旦发现不相等,改成 `false``break`
第四步,别忘了字符串可能有空格,但 GESP 题目一般用 `cin` 读单个词即可。
### 伪代码
**字符串逆序**
```
1. 输入 s
2. for i = s.length()-1 递减到 0
3. 输出 s[i]
4. 输出换行
```
**回文判断**
```
1. 输入 s
2. n = s.length()
3. isPalindrome = true
4. for i = 0 到 n/2 - 1
5. if s[i] != s[n-1-i]
6. isPalindrome = false
7. break
8. if isPalindrome
9. 输出 "是回文"
10. else
11. 输出 "不是回文"
```
### 真实代码
**字符串逆序**
```cpp
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
cin >> s;
for (int i = s.length() - 1; i >= 0; i--) {
cout << s[i];
}
cout << endl;
return 0;
}
```
**回文判断**
```cpp
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
cin >> s;
int n = s.length();
bool isPalindrome = true;
for (int i = 0; i < n / 2; i++) {
if (s[i] != s[n - 1 - i]) {
isPalindrome = false;
break;
}
}
if (isPalindrome) {
cout << "是回文" << endl;
} else {
cout << "不是回文" << endl;
}
return 0;
}
```
### 注意点
1. **逆序遍历注意边界**`for (i = s.length()-1; i >= 0; i--)`,不能忘了减 1否则越界。
2. **回文只需比一半**:循环 `n/2` 次就够了。如果 n=5比下标 (0,4) 和 (1,3),中间的下标 2 不用比。
3. **大小写问题**:严格判断回文时 "Aba" 不是回文A ≠ a。如果要忽略大小写可以先用 `tolower`/`toupper` 统一转换。
4. **string 可以直接用 `==` 比较**`s1 == s2` 判断两个字符串是否完全相等,比字符数组方便得多。
---
## 字符数组 vs string 速查
| 特性 | 字符数组 `char[]` | string 类 |
|------|-------------------|-----------|
| 头文件 | 不需要(或 `<cstring>` | `<string>` |
| 定义 | `char str[100];` | `string s;` |
| 输入 | `cin >> str;` | `cin >> s;` |
| 读整行 | `cin.getline(str, 100);` | `getline(cin, s);` |
| 长度 | `strlen(str)` | `s.length()``s.size()` |
| 拼接 | `strcat(str1, str2);` | `s1 + s2` |
| 比较 | `strcmp(str1, str2)` | `s1 == s2` |
| 结尾标志 | `'\0'` | 不需要关心 |
> **建议**GESP 考试中优先用 `string`,操作更简单,不容易出错。
---
## 本课打油诗
> string 类真好用,拼接查找样样通。
> getline 读整行length 长度记心中。
> 下标访问像数组,逆序只需倒着走。
> 回文首尾比一半find 定位快如风。
---
## 课后作业
1. 输入一个字符串,统计其中数字、字母、其他字符的个数
2. 输入一个字符串,删除其中所有的空格后输出
3. 输入两个字符串 A 和 B判断 B 是否是 A 的子串
4. 输入一个字符串,将其中的数字提取出来组成新字符串并输出