Appearance
第7章 字符串及其函数
🎯 本章学习目标
学完本章,你将能够:
- 理解字符与 ASCII 码的关系,记住三个锚点:'A'=65、'a'=97、'0'=48
- 掌握大小写字母的转换方法(手动加减32、tolower/toupper函数)
- 区分 C 风格字符数组与 C++ string 类的不同
- 熟练使用 string 的基本操作:长度、下标访问、拼接、比较
- 掌握字符串搜索函数(find/rfind)及找不到时的返回值 string::npos
- 熟练使用 substr 截取子串(注意第二个参数是长度)
- 掌握字符串分割的两种方法:手动 find+substr、stringstream
- 掌握字符串替换、插入、删除等修改操作
- 能够独立完成经典字符串编程题:单词统计、回文判断、信息提取、凯撒密码
- 理解 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. 综合题套路:先清洗(过滤+归一化),再处理 │
└──────────────────────────────────────────────────────┘