Skip to content

第7章 字符串及其函数 ​

🎯 本章学习目标 ​

学完本章,你将能够:

  1. 理解字符与 ASCII 码的关系,记住三个锚点:'A'=65、'a'=97、'0'=48
  2. 掌握大小写字母的转换方法(手动加减32、tolower/toupper函数)
  3. 区分 C 风格字符数组与 C++ string 类的不同
  4. 熟练使用 string 的基本操作:长度、下标访问、拼接、比较
  5. 掌握字符串搜索函数(find/rfind)及找不到时的返回值 string::npos
  6. 熟练使用 substr 截取子串(注意第二个参数是长度)
  7. 掌握字符串分割的两种方法:手动 find+substr、stringstream
  8. 掌握字符串替换、插入、删除等修改操作
  9. 能够独立完成经典字符串编程题:单词统计、回文判断、信息提取、凯撒密码
  10. 理解 cin>> 与 getline 的区别,掌握 cin.ignore() 的使用场景

🗺️ 本章知识图谱 ​

字符串及其函数
├── 字符基础
│   ├── char 本质是 1 字节整数
│   ├── ASCII 码锚点:'A'=65 'a'=97 '0'=48
│   └── 大小写差值:32
├── 两种字符串
│   ├── C风格:char[] + '\0'结尾
│   └── C++ string 类(推荐)
├── 输入方式
│   ├── cin >> s(遇空格停止)
│   ├── getline(cin, s)(读整行)
│   └── cin.ignore()(混写时清除换行)
├── 大小写转换
│   ├── 手动:+32 / -32
│   └── 函数:tolower() / toupper()
│       └── <cctype> 头文件
├── 字符判断函数
│   ├── isalpha(字母)
│   ├── isdigit(数字)
│   ├── isalnum(字母或数字)
│   ├── isupper / islower
│   └── isspace(空白字符)
├── 搜索与截取
│   ├── find(从左找,返回首次位置)
│   ├── rfind(从右找,返回末次位置)
│   ├── string::npos(找不到的返回值)
│   ├── find_first_of / find_last_of
│   └── substr(pos, len)(截取子串)
├── 分割
│   ├── 手动 split(find + substr)
│   └── stringstream(>> 或 getline)
├── 修改操作
│   ├── replace(pos, len, str)(替换)
│   ├── insert(pos, str)(插入)
│   ├── erase(pos, len)(删除)
│   └── + 拼接
└── 经典应用
    ├── 统计单词数
    ├── 回文判断(双指针)
    ├── 信息提取(身份证/学号)
    ├── 密码强度校验
    └── 凯撒密码(循环移位)

一、字符与 ASCII 码 ​

1.1 char 类型与 ASCII 码表 ​

在 C++ 中,字符类型 char 本质上是一个 1 字节的整数,存储的就是字符的 ASCII 编码。

字符范围ASCII 码记忆要点
'0' ~ '9'48 ~ 57数字字符不是数字本身!'0' 是 48
'A' ~ 'Z'65 ~ 90大写字母从 65 开始
'a' ~ 'z'97 ~ 122小写字母从 97 开始
空格 ' '32—
换行 '\n'10控制字符

🔥 高频考点:大小写字母的 ASCII 码相差 32! 'a' - 'A' = 97 - 65 = 32。 大写转小写:c + 32;小写转大写:c - 32。考试中给你一个字符的 ASCII 码让你推算另一个,是最经典的考法。

C++ 示例:

cpp
#include <iostream>
using namespace std;

int main() {
    char c1 = 'A';
    cout << (int)c1 << endl;        // 输出 65:char 强转为 int,看到 ASCII 码
    cout << (char)97 << endl;       // 输出 a:int 强转为 char,看到字符

    // 已知 'A' = 65,推算 'D'
    cout << (char)('A' + 3) << endl; // 输出 D

    // 利用差值 32 手动转换大小写
    char lower = 'b';
    char upper = lower - 'a' + 'A'; // 等价于 lower - 32
    cout << upper << endl;           // 输出 B

    // 数字字符转数值:'7' - '0' = 7
    char digit = '7';
    cout << digit - '0' << endl;     // 输出 7(整数)
    return 0;
}

1.2 C 风格字符数组 vs C++ string 类 ​

C++ 中有两种表示字符串的方式:

对比项C 风格 char[]C++ string 类
本质字符数组,以 '\0' 结尾标准库类,自动管理内存
长度需手动记录或用 strlen()s.length() / s.size()
拼接strcpy/strcat(易错)直接用 +
修改可直接改数组元素可直接用 s[i] 修改
推荐度三级了解即可⭐ 强烈推荐使用
cpp
#include <iostream>
#include <string>
#include <cstring>
using namespace std;

int main() {
    char a[20] = "hello";   // C 风格:字符数组,末尾自动补 '\0'
    string s = "hello";     // C++ 风格:string 类

    cout << strlen(a) << endl;  // 5:C 风格求长度
    cout << s.length() << endl; // 5:C++ 求长度
    cout << s.size() << endl;   // 5:length() 和 size() 完全等价

    cout << s[0] << endl;       // h:可以像数组一样用下标访问
    s[0] = 'H';                 // 也可以直接修改
    cout << s << endl;          // Hello
    return 0;
}

1.3 字符串输入:cin >>、getline() 与 cin.ignore() ​

这是三级考试中最容易踩坑的知识点之一:

  • cin >> s:从非空白字符开始读,遇到空格/换行/制表符就停止,读不到带空格的整行
  • getline(cin, s):读取一整行,直到换行符为止(换行符被丢弃,不存入字符串)
cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    int age;
    string name;

    cin >> age;          // 输入:15 回车
    cin.ignore();        // ⚠️ 关键!丢弃缓冲区中残留的换行符
    getline(cin, name);  // 输入:Zhang San 回车(含空格也能完整读入)

    cout << age << endl;   // 15
    cout << name << endl;  // Zhang San
    return 0;
}

⚠️ 高频易错点: cin >> 读取数字后,回车键产生的换行符 '\n' 会残留在输入缓冲区中。如果紧接着调用 getline(),它会读到这个空行,直接返回空字符串!解决办法:在中间加一句 cin.ignore(); 把残留的换行符"吃掉"。


二、大小写转换与字符判断 ​

2.1 C++:tolower() / toupper() 与 <cctype> ​

C++ 的 <cctype> 头文件提供了一整套字符处理函数:

函数作用返回真的条件
tolower(c)转小写字母返回转换后的字符(int 形式)
toupper(c)转大写字母返回转换后的字符(int 形式)
isalpha(c)是否字母a-z 或 A-Z
isdigit(c)是否数字字符0-9
isalnum(c)是否字母或数字字母 或 数字
isupper(c)是否大写字母A-Z
islower(c)是否小写字母a-z
isspace(c)是否空白字符空格、'\n'、'\t' 等
ispunct(c)是否标点符号! , . ? 等
cpp
#include <iostream>
#include <string>
#include <cctype>
using namespace std;

int main() {
    string s = "Hello, World! 2025";

    // 1. 全部转小写
    string lowerS = s;
    for (int i = 0; i < (int)lowerS.size(); i++) {
        lowerS[i] = tolower(lowerS[i]);  // 逐个字符转换
    }
    cout << lowerS << endl;   // hello, world! 2025

    // 2. 全部转大写
    string upperS = s;
    for (char &ch : upperS) { // 引用遍历,直接修改原字符
        ch = toupper(ch);
    }
    cout << upperS << endl;   // HELLO, WORLD! 2025

    // 3. 统计各类字符个数
    int letter = 0, digit = 0, space = 0, other = 0;
    for (char ch : s) {
        if (isalpha(ch))      letter++;
        else if (isdigit(ch)) digit++;
        else if (isspace(ch)) space++;
        else                  other++;
    }
    cout << letter << " " << digit << " " << space << " " << other << endl;
    // 输出:10 4 2 2  (10个字母,4个数字,2个空格,逗号和感叹号2个其他字符)
    return 0;
}

⚠️ 易错点:tolower()/toupper() 的返回值是 int 类型,赋值给 char 时会自动转换,直接用即可;但如果直接 cout << tolower('A'),输出的是数字 97 而不是字符 'a'!


三、字符串搜索与截取 ​

3.1 C++ find() 系列函数 ​

函数作用查找方向
s.find(str)查找子串第一次出现的位置从左往右
s.rfind(str)查找子串最后一次出现的位置从右往左
s.find_first_of(chars)查找字符集合中任意一个字符中第一次出现的位置从左往右
s.find_last_of(chars)字符集合中任意字符最后一次出现的位置从右往左
s.find_first_not_of(chars)第一个不属于字符集合的字符位置从左往右

🔥 最高频考点:找不到时返回 string::npos!
find() 的返回类型是 size_t(无符号整数),找到返回起始下标,找不到返回特殊常量 string::npos(值为 (size_t)-1,即一个极大的数)。 判断时必须写 if (pos == string::npos),不要写 if (pos == -1)。虽然由于类型转换 pos == -1 有时也能成立,但 string::npos 才是标准的、可移植的写法。

cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s = "abracadabra";

    size_t p1 = s.find("ra");       // 从左找 "ra"
    cout << p1 << endl;              // 2(下标2处是 "ra")

    size_t p2 = s.rfind("ra");      // 从右找 "ra"
    cout << p2 << endl;              // 9

    size_t p3 = s.find("xyz");      // 找不到
    cout << (p3 == string::npos) << endl;  // 1(true),标准判断方式

    size_t p4 = s.find_first_of("bcd");  // 找 b/c/d 中任意一个最早出现的
    cout << p4 << endl;              // 1(下标1是 'b')

    // find 还可以带第二个参数:从指定位置开始找
    size_t p5 = s.find("ra", 3);    // 从下标3开始往后找
    cout << p5 << endl;              // 9
    return 0;
}

3.2 C++ substr() 截取子串 ​

cpp
string s = "Hello, World";

string sub1 = s.substr(7);       // 从下标7截取到末尾:"World"
string sub2 = s.substr(0, 5);    // 从下标0开始,截取5个字符:"Hello"
string sub3 = s.substr(7, 3);    // 从下标7开始,截取3个字符:"Wor"
写法含义
s.substr(pos)从 pos 一直截取到字符串末尾
s.substr(pos, len)从 pos 开始,截取 len 个字符

⚠️ 易错点:substr 的两个参数是**(起始位置,长度)**,不是(起始位置,结束位置)!


四、字符串分割 ​

4.1 C++ 手动实现 split(find + substr) ​

C++ 的 string 类没有现成的 split 函数,三级考试要求会用 find + substr 手动实现:

📚 拓展 · 超纲:示例中的 vector 属于 STL 容器,不在三级考纲范围内(STL 属七级内容)。这里用它只是为了演示"分割结果往哪里存";三级考试中可以直接用 string parts[100]; 配合计数变量代替,不必使用 vector。

cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;

// 按分隔符 delim 分割字符串,结果存入 vector
vector<string> split(const string& s, char delim) {
    vector<string> result;
    size_t start = 0;          // 每段的起始位置
    size_t pos;                // 分隔符的位置
    while ((pos = s.find(delim, start)) != string::npos) {
        result.push_back(s.substr(start, pos - start)); // 截取一段
        start = pos + 1;       // 下一段从分隔符后开始
    }
    result.push_back(s.substr(start)); // 别忘了最后一段!
    return result;
}

int main() {
    string s = "2025-06-14";
    vector<string> parts = split(s, '-');
    for (const string& part : parts) {
        cout << part << " ";     // 2025 06 14
    }
    cout << endl;
    return 0;
}

4.2 C++ stringstream 按分隔符读取 ​

📚 拓展 · 超纲:<sstream> 的 stringstream 同样属于 C++ 标准库工具,不在三级考纲内。这里作为"更省事的分割写法"介绍;考试中优先掌握 §4.1 的 find + substr 手写法。

<sstream> 头文件提供了更简洁的方式:按空白符分割直接用 >>;按指定分隔符分割用 getline:

cpp
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

int main() {
    // 方式一:按空白(空格/换行/制表符)分割,>> 自动跳过连续空白
    string s1 = "  hello   world  foo ";
    stringstream ss1(s1);
    string word;
    while (ss1 >> word) {        // 每次读取一个单词
        cout << word << "/";      // hello/world/foo/
    }
    cout << endl;

    // 方式二:按指定字符(如逗号)分割
    string s2 = "apple,banana,orange";
    stringstream ss2(s2);
    string item;
    while (getline(ss2, item, ',')) {  // 以逗号为界读取
        cout << item << " ";           // apple banana orange
    }
    cout << endl;
    return 0;
}

五、字符串替换与其他常用操作 ​

5.1 C++ 修改类函数大全 ​

操作C++ 写法说明
求长度s.length() / s.size()两者完全等价
拼接s1 + s2直接用加号
替换s.replace(pos, len, str)从 pos 起的 len 个字符替换为 str
插入s.insert(pos, str)在 pos 位置前插入 str
删除s.erase(pos, len)从 pos 起删除 len 个字符
比较s1.compare(s2)相等返回0,s1 大返回正数,小返回负数
判空s.empty()空串返回 true
cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s = "I like cat";

    // 替换:把从下标7开始的3个字符 "cat" 替换为 "dog"
    s.replace(7, 3, "dog");
    cout << s << endl;        // I like dog

    // 插入:在下标0处插入
    s.insert(0, "Hello! ");
    cout << s << endl;        // Hello! I like dog

    // 删除:从下标0开始删除7个字符
    s.erase(0, 7);
    cout << s << endl;        // I like dog

    // 拼接
    string s2 = s + " very much";
    cout << s2 << endl;       // I like dog very much

    // 比较:按字典序
    cout << ("apple" < "banana") << endl;   // 1:string 可直接用 < > == 比较
    cout << s.compare("I like dog") << endl;  // 0:相等返回0
    return 0;
}

⚠️ 易错点:replace(pos, len, str) 的第二个参数是被替换的字符个数,不是替换的终点下标。


六、字符串处理总结表 ⭐ ​

考前必看的一张表,把对应关系一次理清:

功能C++
定义字符串string s = "hi";
求长度s.length() / s.size()
取第 i 个字符s[i]
拼接s1 + s2
转小写tolower(s[i])(逐字符)
转大写toupper(s[i])(逐字符)
大小写互换手动实现
判断字母isalpha(c)
判断数字isdigit(c)
查找子串s.find(sub)
找不到时返回 string::npos
从右查找s.rfind(sub)
判断包含s.find(sub) != npos
截取子串s.substr(pos, len)
分割手动 split / stringstream
连接循环 +=
替换s.replace(pos,len,str)
插入s.insert(pos, str)
删除s.erase(pos, len)
去空白手动实现
整行输入getline(cin, s)
判断前缀/后缀s.find(...)==0 / 手动

七、经典例题精讲 ​

例题 1:统计单词数 ​

题目: 输入一行可能包含多个空格的英文句子,统计其中单词的个数。

思路: 按空白分割后数段数即可。C++ 用 stringstream 的 >> 自动跳过连续空格;

cpp
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

int main() {
    string line;
    getline(cin, line);          // 读取整行句子

    stringstream ss(line);
    string word;
    int count = 0;
    while (ss >> word) {         // >> 自动跳过任意数量的空白
        count++;
    }
    cout << count << endl;
    return 0;
}
// 输入:  I   love GESP   (前后和中间都有多个空格)
// 输出:3

例题 2:回文串判断 ​

题目: 判断一个字符串是否为回文(正读反读都一样,如 level、上海自来水来自海上),忽略大小写。

思路: 双指针从两端向中间比较。

cpp
#include <iostream>
#include <string>
#include <cctype>
using namespace std;

int main() {
    string s;
    cin >> s;

    bool ok = true;
    int i = 0, j = (int)s.size() - 1;
    while (i < j) {
        // 统一转小写后再比较,实现"忽略大小写"
        if (tolower(s[i]) != tolower(s[j])) {
            ok = false;          // 对称位置字符不同,不是回文
            break;
        }
        i++;
        j--;
    }
    cout << (ok ? "Yes" : "No") << endl;
    return 0;
}
// 输入:Level    输出:Yes
// 输入:hello    输出:No

例题 3:身份证信息提取 ​

题目: 输入 18 位身份证号,提取出生年月日,并判断性别(第 17 位奇数为男,偶数为女)。

知识点: 身份证第 7~14 位是出生年月日(下标 6~13),第 17 位(下标 16)是顺序码,奇数男、偶数女。

cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    string id;
    cin >> id;                    // 例:110101199003071234

    // substr(起始下标, 长度):第7位开始共8位是出生日期
    string birth = id.substr(6, 8);       // 19900307
    string year  = id.substr(6, 4);       // 1990
    string month = id.substr(10, 2);      // 03
    string day   = id.substr(12, 2);      // 07
    cout << year << "-" << month << "-" << day << endl;

    // 第17位(下标16):奇数男,偶数女
    char genderCode = id[16];
    if ((genderCode - '0') % 2 == 1)
        cout << "男" << endl;
    else
        cout << "女" << endl;
    return 0;
}
// 输出:
// 1990-03-07
// 男

🔥 考试常见考法 :学号、身份证、编号类题目,本质都是"按下标提取子串"。记住 substr(起点, 长度),这类题就是送分题。

例题 4:密码强度校验 ​

题目: 判断密码强度:长度至少 8 位,且同时包含大写字母、小写字母和数字,满足则输出 Strong,否则输出 Weak。

cpp
#include <iostream>
#include <string>
#include <cctype>
using namespace std;

int main() {
    string pwd;
    cin >> pwd;

    bool hasUpper = false, hasLower = false, hasDigit = false;
    for (char c : pwd) {
        if (isupper(c)) hasUpper = true;
        if (islower(c)) hasLower = true;
        if (isdigit(c)) hasDigit = true;
    }

    if ((int)pwd.length() >= 8 && hasUpper && hasLower && hasDigit) {
        cout << "Strong" << endl;
    } else {
        cout << "Weak" << endl;
    }
    return 0;
}
// 输入:Abc12345    输出:Strong
// 输入:abc12345    输出:Weak(没有大写字母)
// 输入:Abc123      输出:Weak(不足8位)

例题 5:字符串加密(凯撒密码) ​

题目: 凯撒密码是一种古老的加密方法:把每个字母在字母表中向后移动固定位数(如 3 位),a→d,x→a(循环绕回),非字母字符保持不变。请实现加密程序。

知识点: 字母循环移位的核心公式:'a' + (c - 'a' + shift) % 26。

cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s;
    getline(cin, s);
    int shift = 3;                       // 移位位数

    for (char &c : s) {
        if (c >= 'a' && c <= 'z') {
            // 小写字母:先转为0~25的序号,加位移后对26取模实现循环
            c = 'a' + (c - 'a' + shift) % 26;
        } else if (c >= 'A' && c <= 'Z') {
            c = 'A' + (c - 'A' + shift) % 26;
        }
        // 非字母字符(空格、数字、标点)保持不变
    }
    cout << s << endl;
    return 0;
}
// 输入:Hello, World!
// 输出:Khoor, Zruog!

⚠️ **易错点:**移位必须用 % 26 取模!否则 z 往后移会移出字母范围。解密时把 + shift 改成 - shift(或 + 26 - shift)即可。


八、📝 实战练习 ​

练习题 1(单选题) ​

题目: 已知大写字母 'A' 的 ASCII 码为 65,小写字母 'a' 为 97。执行以下 C++ 代码后,输出结果是( )

cpp
char c = 'G';
cout << (char)(c + 32) << " " << (int)('0' + 5) << endl;

A. g 53 B. G 5 C. g 5 D. G 53

🔑 点击查看答案与解析

答案: A

解析:

  • 'G' 的 ASCII 码是 71,71 + 32 = 103,103 对应小写字母 'g',强转为 char 输出 g。
  • '0' 的 ASCII 码是 48,48 + 5 = 53,强转为 int 输出数字 53。
  • 注意:(int)('0' + 5) 输出的是 ASCII 码 53,而不是字符 '5'。选 A。

练习题 2(程序阅读题) ​

题目: 阅读以下 C++ 程序,写出运行结果:

cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s = "abracadabra";
    cout << s.find("ra") << endl;
    cout << s.rfind("ra") << endl;
    cout << s.find("cat") << endl;
    cout << s.substr(4, 3) << endl;
    return 0;
}
🔑 点击查看答案与解析

答案:

2
9
18446744073709551615
cad

解析:

  • s.find("ra"):"abracadabra" 中 "ra" 第一次出现在下标 2(a-b-r-a...,下标 2、3 是 r、a)。
  • s.rfind("ra"):最后一次出现在下标 9(末尾的 "ra")。
  • s.find("cat"):找不到,返回 string::npos。size_t 是 64 位无符号整数,npos 的值为 (size_t)-1 = 18446744073709551615(即 2⁶⁴-1)。考试中若以判断题形式出现,要知道它是一个"极大的数",而不是 -1。
  • s.substr(4, 3):从下标 4 开始截取 3 个字符,下标 4、5、6 是 c、a、d,输出 cad。

⚠️ 提醒:实际判题时,string::npos 常以"找不到时 find 的返回值"为考点,记住用 == string::npos 判断即可。


练习题 3(编程题) ​

题目: 输入一行字符(可能含空格),分别统计其中英文字母、数字字符、空格和其他字符的个数并输出。

🔑 点击查看答案与解析

参考代码(C++):

cpp
#include <iostream>
#include <string>
#include <cctype>
using namespace std;

int main() {
    string s;
    getline(cin, s);               // 整行读取,包含空格

    int letter = 0, digit = 0, space = 0, other = 0;
    for (char c : s) {
        if (isalpha(c))      letter++;
        else if (isdigit(c)) digit++;
        else if (c == ' ')    space++;   // 题目只统计空格,用 isspace 也可
        else                  other++;
    }
    cout << "字母:" << letter << " 数字:" << digit
         << " 空格:" << space << " 其他:" << other << endl;
    return 0;
}
// 输入:Hello 2025!
// 输出:字母:5 数字:4 空格:1 其他:1

解析要点: ① 含空格的输入用 getline(C++);② C++ 用 <cctype> 的分类函数逐字符判断。


练习题 4(编程题) ​

题目: 输入一个字符串,判断它是否为回文。只考虑其中的字母和数字字符,忽略大小写,并忽略所有标点和空格。例如 "A man, a plan, a canal: Panama" 是回文。

🔑 点击查看答案与解析

参考代码(C++):

cpp
#include <iostream>
#include <string>
#include <cctype>
using namespace std;

int main() {
    string s;
    getline(cin, s);

    // 第一步:清洗——只保留字母和数字,统一转小写
    string t;
    for (char c : s) {
        if (isalnum(c)) {              // isalnum:字母或数字
            t += tolower(c);
        }
    }
    // 第二步:双指针判断回文
    bool ok = true;
    int i = 0, j = (int)t.size() - 1;
    while (i < j) {
        if (t[i] != t[j]) { ok = false; break; }
        i++;
        j--;
    }
    cout << (ok ? "Yes" : "No") << endl;
    return 0;
}
// 输入:A man, a plan, a canal: Panama
// 输出:Yes

解析要点: 这类"先清洗、再判断"的两步法是字符串综合题的通用套路:先用 isalnum 过滤 + tolower 归一化,再用双指针(或逆序比较)判断。


练习题 5(程序阅读题) ​

题目: 阅读以下 C++ 程序,若输入 25 回车后再输入 Hello World 回车,程序输出的第二行是( )

cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    int age;
    string name;
    cin >> age;
    getline(cin, name);
    cout << age << endl;
    cout << "[" << name << "]" << endl;
    return 0;
}

A. [Hello World]  B. [Hello]  C. [](空串)  D. 编译错误

🔑 点击查看答案与解析

答案: C

解析: 本题考查 cin >> 和 getline 混用时的经典坑。

  • cin >> age 读取了数字 25,但用户输入的回车符 \n 留在了输入缓冲区中。
  • 紧接着的 getline(cin, name) 一上来就遇到了这个 \n,认为已经到了行尾,于是直接返回空字符串。
  • 所以 name 是空串,输出 []。

正确写法应该在 cin >> age 之后加一句 cin.ignore(); 把残留的换行符"吃掉",getline 才能正常读取下一行。

选 C。

⚠️ 高频易错点:这是字符串输入题最经典的坑。只要看到 cin >> 后面跟着 getline,第一反应就是——会不会少了 cin.ignore()?


练习题 6(程序阅读题) ​

题目: 阅读以下 C++ 程序,输出结果是( )

cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s = "hello, world!";
    s.replace(7, 5, "GESP");
    s.erase(5, 1);
    cout << s << endl;
    return 0;
}

A. helloGESP!  B. hello, GESP!  C. hello GESP!  D. hellGESP!

🔑 点击查看答案与解析

答案: C

解析: 逐步追踪字符串变化:

第 1 步:初始字符串

h e l l o ,   w o r l d !
0 1 2 3 4 5 6 7 8 9 10 11 12

第 2 步:s.replace(7, 5, "GESP")

  • 从下标 7 开始的 5 个字符:world(w=7, o=8, r=9, l=10, d=11)
  • 替换为 "GESP"(4 个字符)
  • 结果:"hello, GESP!"
h e l l o ,   G E S P !
0 1 2 3 4 5 6 7 8 9 10 11

第 3 步:s.erase(5, 1)

  • 从下标 5 开始删除 1 个字符:下标 5 是逗号 ,
  • 删除后:"hello GESP!"
h e l l o   G E S P !
0 1 2 3 4 5 6 7 8 9 10

最终输出 hello GESP!,选 C。

⚠️ 易错提醒:replace 和 erase 的第二个参数都是长度,不是结束位置。replace(pos, len, str) 和 erase(pos, len)。erase 删除后,后面的字符会往前挪,下标会变化——不过本题 erase 在 replace 之后执行,各自独立,所以不会混淆。


练习题 7(判断题) ​

题目: 判断下列说法是否正确,正确打"√",错误打"×"。

(1) C++ 中可以用 == 直接比较两个 string 的内容是否相等。( ) (2) s.substr(3) 的作用是取从下标 3 开始到字符串末尾的子串。( ) (3) string::npos 的值等于 -1(int 类型)。( ) (4) C 风格字符串以 '\0' 结尾。( ) (5) tolower('A') 的返回值是字符 'a',可以直接 cout 输出字符 a。( ) (6) s.length() 和 s.size() 功能完全相同。( )

🔑 点击查看答案与解析

答案: (1) √ (2) √ (3) × (4) √ (5) × (6) √

解析:

  • (1) √:C++ 的 string 类重载了 == 运算符,可以直接比较内容。
  • (2) √:substr 只有一个参数时,从指定位置一直截取到末尾。
  • (3) ×:string::npos 的类型是 size_t(无符号整数),值为 (size_t)-1(即一个极大的数),不是 int 类型的 -1。判断时用 pos == string::npos。
  • (4) √:C 风格字符串(char 数组)依靠末尾的 '\0' 标记结束。
  • (5) ×:tolower 返回值是 int 类型,直接 cout 输出的是 ASCII 码值(整数 97),不是字符。要强转为 char 才能输出 'a'。
  • (6) √:length() 和 size() 在 string 中完全等价,都是返回字符个数。

练习题 8(编程题) ​

题目: 输入一个字符串,将其中所有的小写字母 a 替换为大写字母 A,其他字符保持不变。输出替换后的字符串。

输入样例:

banana

输出样例:

bAnAnA
🔑 点击查看答案与解析

参考代码(C++,方法一:遍历替换):

cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s;
    cin >> s;

    for (int i = 0; i < (int)s.size(); i++) {
        if (s[i] == 'a') {
            s[i] = 'A';       // 找到小写a,替换为大写A
        }
    }

    cout << s << endl;
    return 0;
}

参考代码(方法二:用 find 循环查找 + replace):

cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s;
    cin >> s;

    size_t pos = 0;
    while ((pos = s.find('a', pos)) != string::npos) {
        s.replace(pos, 1, "A");  // 从pos开始的1个字符替换为"A"
        pos++;                   // 从下一个位置继续找
    }

    cout << s << endl;
    return 0;
}

解析:

  • 方法一最直观:遍历字符串每个字符,遇到 'a' 就改成 'A'。思路简单、代码最短,推荐使用。
  • 方法二用 find + replace 组合:find 找到目标字符的位置,replace 替换。适合子串替换(替换多个字符)的场景,单字符替换不如方法一直观。

验证:banana → 把每个 a 换成 A → bAnAnA ✓

💡 扩展:如果题目是"将所有的 "abc" 替换为 "xyz"",就必须用方法二的 find+replace 模式了。单字符替换用遍历法最简洁。


九、📊 考点总结表 ​

考点重要程度出题形式易错点
ASCII 码与大小写差值 32⭐⭐⭐⭐选择/程序阅读记错三个锚点值;数字字符和数字本身搞混
cin>> 与 getline 及 cin.ignore()⭐⭐⭐⭐程序阅读/编程混写时忘记 ignore;不知道 getline 会读空行
tolower/toupper 与 <cctype> 判断函数⭐⭐⭐⭐选择/编程以为 tolower 返回 char(实际是 int);函数名拼错
find/rfind 与 string::npos⭐⭐⭐⭐⭐选择/程序阅读(必考)以为找不到返回 -1;npos 类型是 size_t 不是 int
substr(pos, len) 截取⭐⭐⭐⭐程序填空/编程第二个参数记成结束位置(实际是长度)
字符串分割(手动 split / stringstream)⭐⭐⭐编程忘记最后一段;分隔符处理不当
replace/insert/erase⭐⭐⭐程序阅读参数含义记混;都是(位置, 长度)
string 比较(==、<、>)⭐⭐⭐选择/判断以为不能直接比较(C++ string 可以)
回文、单词统计、信息提取、凯撒密码⭐⭐⭐⭐⭐编程大题边界条件(双指针相遇条件);移位忘了取模

⭐ 必背速记卡 ​

┌──────────────────────────────────────────────────────┐
│              ⭐ 字符串必背速记卡                         │
├──────────────────────────────────────────────────────┤
│  1. 'A'=65  'a'=97  '0'=48,大小写字母差 32           │
│  2. cin>> 遇空格停止;getline 读整行                   │
│  3. cin >> 后接 getline 必须加 cin.ignore() 吃换行     │
│  4. find 找不到返回 string::npos,用 == npos 判断     │
│  5. substr(起点, 长度)——第二个参数是长度不是终点       │
│  6. replace(pos, len, str) / erase(pos, len)          │
│  7. string 可直接用 == < > 比较内容(字典序)          │
│  8. tolower/toupper 返回 int,强转 char 才输出字符      │
│  9. C风格字符串以 '\0' 结尾;string 自动管理           │
│ 10. length() 和 size() 完全等价                       │
│ 11. 回文判断:双指针从两端向中间比较                    │
│ 12. 凯撒密码:'a' + (c - 'a' + shift) % 26            │
│ 13. 综合题套路:先清洗(过滤+归一化),再处理           │
└──────────────────────────────────────────────────────┘

百炼成钢,融会贯通