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

9.9 KiB
Raw Blame History

第8课字符数组与string

教学目标

  • 掌握 string 类的基本操作(长度、访问、截取、查找)
  • 理解字符数组与 string 的区别
  • 能处理字符串遍历、逆序、回文判断等常见问题
  • 会用 getline 读取含空格的字符串

核心考点:字符串输入和逐字符处理

考什么

常见题型包括读取一句话、统计各类字符、查找或替换子串、反转字符串和判断回文。首先要正确读入完整字符串,然后通过下标逐个处理字符。

必须理解

cin >> s 遇到空格就停止,只适合读取一个不含空格的单词;getline(cin, s) 会读取整行。如果前面刚用 cin >> 读取数字,输入缓冲区可能残留换行符,应先用 cin.ignore() 清掉,否则 getline 会读到空行。字符串长度为 s.length(),合法下标仍是 0~length-1。逐字符处理时,每个 s[i] 都是 char,可配合 isdigitisalpha 等函数分类。

核心写法

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 读取句子,只得到第一个单词。
  • 混用 cingetline 时忘记处理残留换行。
  • 写成 i <= s.length(),访问字符串末尾之外的位置。
  • 把字符下标与子串位置混淆,或未检查 find 是否找到。

判断是否掌握

能根据输入是否含空格选择 cingetline,能解释空行问题,并能无越界地遍历字符串完成字符统计或修改。


题目一:字符串输入输出与字符统计

核心代码(一句话)

字符串处理的核心:getline(cin, s) 读整行s.length() 取长度s[i] 访问每个字符,配合字符判断函数做统计。

自然语言思路

输入一句话,统计里面的大写字母、小写字母、数字、其他字符各有几个。

第一步,用 getline(cin, s) 读入整行(不能用 cin >> s,因为碰到空格就停了)。 第二步,用 s.length()s.size() 拿到字符串长度。 第三步,用 for 循环遍历每个字符 s[i]。 第四步,对每个字符用判断函数分类:isupper() 判断大写、islower() 判断小写、isdigit() 判断数字,剩下的就是"其他"。 第五步,四个计数器分别累加,最后输出。

提示:isupperislowerisdigit 这些函数在第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

真实代码

#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. 访问越界:下标范围 0s.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.     输出 "未找到"

真实代码

#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",是一个特殊常量,表示"没找到"。不要用 -10 来判断。
  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。一旦发现不相等,改成 falsebreak。 第四步,别忘了字符串可能有空格,但 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.    输出 "不是回文"

真实代码

字符串逆序

#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;
}

回文判断

#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. 输入一个字符串,将其中的数字提取出来组成新字符串并输出