Files
project1/第08课-字符串与string/教案-教师版.md

302 lines
9.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第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. 输入一个字符串,将其中的数字提取出来组成新字符串并输出