7 string字符串及其函数(一)
正在加载视频播放器…
GESP C++ 三级:string 字符串及其函数
核心目标:掌握
string类型的定义、初始化、输入输出,熟练使用size/length、substr、find、insert、erase、replace、append等核心函数,理解string与char数组的区别,能综合运用字符串函数配合枚举法和模拟法解决实际问题。
string是会自动管理内存的字符串类型;下标从 0 开始,find找不到返回string::npos,substr(pos, len)从 pos 开始截取 len 个字符;遍历前先确认下标范围,空字符串和单字符必须单独验证。
一、string 的定义与初始化
需要包含头文件:
#include <string>
using namespace std;
多种创建方式:
string s1; // 空字符串 ""
string s2("Hello GESP"); // 直接初始化
string s3(s2); // 拷贝初始化
string s4(5, 'A'); // 重复字符 "AAAAA"
string s5 = "C++三级"; // 赋值初始化
输入输出:
string s;
cin >> s; // 读入一个单词(遇到空格停止)
getline(cin, s); // 读入一整行(包括空格)
cout << s << endl; // 输出字符串
重点:
cin >> s遇到空格就停止,如果题目要求读入含空格的字符串,必须使用getline(cin, s)。
二、string 核心函数详解
1. 长度与判空
string s = "GESP";
cout << s.size() << endl; // 4
cout << s.length() << endl; // 4,与 size() 功能相同
cout << s.empty() << endl; // 0(false),不为空返回 0
s.size()和s.length()功能相同,返回字符个数(不含结束符)。s.empty()返回bool,空字符串返回true。
2. 访问与遍历
string s = "GESP";
cout << s[0] << endl; // 'G',下标从0开始
cout << s.at(1) << endl; // 'E',at()会检查越界
cout << s.front() << endl; // 'G',首字符
cout << s.back() << endl; // 'P',末字符
遍历方式:
// 方式1:下标遍历(最常用)
for (int i = 0; i < s.size(); i++) {
cout << s[i] << " ";
}
// 方式2:范围for循环
for (char c : s) {
cout << c << " ";
}
3. 查找 find
string s = "CCF GESP C++";
size_t pos = s.find("GESP"); // 返回 4
size_t pos2 = s.find("C++"); // 返回 9
size_t pos3 = s.find("Java"); // 返回 string::npos
s.find(sub, pos):从pos位置开始查找子串sub,找到返回起始下标,未找到返回string::npos。string::npos是一个特殊常量,表示“未找到”。
判断是否找到:
if (s.find("GESP") != string::npos) {
cout << "找到了" << endl;
} else {
cout << "没找到" << endl;
}
4. 截取子串 substr
string s = "CCF GESP C++";
string sub1 = s.substr(4, 4); // "GESP",从下标4开始取4个字符
string sub2 = s.substr(9); // "C++",从下标9开始取到末尾
s.substr(pos, len):从pos开始截取长度为len的子串;若省略len则截取到末尾。
5. 插入、删除与替换
string str = "GESP考试";
str.insert(4, " C++"); // 在位置4插入,结果 "GESP C++考试"
str.erase(8, 2); // 从位置8开始删除2个字符,结果 "GESP C++"
str.replace(5, 3, "三级"); // 从位置5开始替换3个字符为"三级"
str.append(" 考试"); // 在末尾追加,结果 "GESP C++三级 考试"
str.push_back('!'); // 在末尾追加一个字符
str.pop_back(); // 删除末尾一个字符
str.clear(); // 清空字符串
s.insert(pos, str):在位置pos插入字符串str。s.erase(pos, len):从pos开始删除len个字符。s.replace(pos, len, str):从pos开始删除len个字符,并插入字符串str。s.append(str):在末尾追加字符串。s.push_back(c):在末尾追加一个字符。s.pop_back():删除末尾一个字符。
6. 字符串拼接
string s1 = "GESP";
string s2 = "C++";
s1 += " "; // 拼接空格
s1 += s2; // 拼接字符串
s1.append(" 三级"); // 使用append
// 结果:"GESP C++ 三级"
7. 字符串比较
string a = "abc";
string b = "abd";
if (a == b) cout << "相等"; // 直接使用 == 比较
if (a < b) cout << "a < b"; // 按字典序比较
8. 类型转换
// 字符串转数字
int num = stoi("42");
double d = stod("3.14");
// 数字转字符串
string s1 = to_string(123);
string s2 = to_string(3.14);
三、字符处理辅助函数
配合字符串遍历使用,需包含头文件 <cctype>:
| 函数 | 功能 | 示例 |
|---|---|---|
isupper(c) |
是否大写字母 | isupper('A') 为真 |
islower(c) |
是否小写字母 | islower('a') 为真 |
isdigit(c) |
是否数字字符 | isdigit('7') 为真 |
isalpha(c) |
是否字母 | isalpha('A') 为真 |
toupper(c) |
转大写 | toupper('a') 返回 'A' |
tolower(c) |
转小写 | tolower('A') 返回 'a' |
示例:统计字符串中大写字母、小写字母和数字的个数
string s;
getline(cin, s);
int upper = 0, lower = 0, digit = 0;
for (char c : s) {
if (isupper(c)) upper++;
else if (islower(c)) lower++;
else if (isdigit(c)) digit++;
}
cout << upper << " " << lower << " " << digit << endl;
四、GESP 三级字符串真题精讲
真题1:字母求和
题目描述:
小杨同学发明了一种新型密码,对于每一个小写英文字母,该小写字母代表了一个正整数,即该字母在字母顺序中的位置,例如字母 a 代表了正整数 1,字母 b 代表了正整数 2;对于每一个大写英文字母,该大写字母代表了一个负整数,即该字母的 ASCII 码的相反数,例如字母 A 代表了负整数 -65。给定一个字符串,计算它对应的加密前的整数。
解题思路:
遍历字符串,根据字符类型(大小写)进行不同的数值映射并求和。
#include <iostream>
#include <string>
using namespace std;
int main() {
int n;
string s;
cin >> n >> s;
int sum = 0;
for (char c : s) {
if (c >= 'a' && c <= 'z') {
// 小写字母:字母位置值(a=1, b=2, ...)
sum += (c - 'a' + 1);
} else if (c >= 'A' && c <= 'Z') {
// 大写字母:ASCII码的相反数
sum -= c; // 等价于 sum += (-c)
}
}
cout << sum << endl;
return 0;
}
核心考点:字符遍历 + 字符类型判断 + ASCII 码运算。
真题2:分割字符串
题目描述:
小明拿到了一段字符串文本,按规则分割:从左到右扫描,第一次遇到1个空格时分割为左右两部分(舍弃空格);对右侧字符串继续扫描,第一次遇到连续2个空格时分割(舍弃这2个空格);下一次需要连续3个空格,以此类推。输出每次分割左侧的字符串,以及最后一次分割的右侧字符串。如果完全无法分割,直接输出原字符串。
解题思路:
这道题是 find 和 substr 的典型应用。核心是用循环逐步查找“连续n个空格”的位置,分割字符串。
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
getline(cin, s);
int need = 1; // 本次需要连续几个空格
int start = 0; // 当前处理的起始位置
bool split = false; // 是否进行过分割
while (true) {
// 构造 need 个连续空格的查找目标
string target(need, ' ');
// 从 start 位置开始查找
size_t pos = s.find(target, start);
if (pos == string::npos) {
// 找不到,无法继续分割
if (!split) {
cout << s << endl; // 完全无法分割,输出原字符串
} else {
cout << s.substr(start) << endl; // 输出最后一部分
}
break;
}
// 输出分割左侧的部分
cout << s.substr(start, pos - start) << endl;
split = true;
// 更新起始位置(跳过分割用的空格)
start = pos + need;
need++; // 下次需要更多空格
}
return 0;
}
关键细节:
| 细节 | 说明 |
|---|---|
s.find(target, start) |
从 start 开始查找目标子串 |
s.substr(start, pos - start) |
截取左侧部分 |
s.substr(start) |
截取从 start 到末尾的部分 |
getline(cin, s) |
输入可能包含空格,必须用 getline |
split 标志 |
记录是否进行过分割,区分输出逻辑 |
| 空行输出 | 如果分割出空字符串,必须输出空行(不能省略) |
真题3:密码合规检测
题目描述:
检查密码有效性,合规密码应满足:① 只能由大小写字母、数字和 !@#$ 组成;② 长度 6~12;③ 大写字母、小写字母和数字至少包含其中两种,且至少包含 !@#$ 中的一个。
解题思路:
多重条件验证,遍历字符串统计各类字符数量,逐一判断规则。
#include <iostream>
#include <string>
using namespace std;
bool isValid(string pwd) {
if (pwd.size() < 6 || pwd.size() > 12) return false;
int upper = 0, lower = 0, digit = 0, special = 0;
for (char c : pwd) {
if (c >= 'a' && c <= 'z') lower++;
else if (c >= 'A' && c <= 'Z') upper++;
else if (c >= '0' && c <= '9') digit++;
else if (c == '!' || c == '@' || c == '#' || c == '$') special++;
else return false; // 含非法字符
}
// 至少包含两种:大小写字母和数字
int types = 0;
if (upper > 0) types++;
if (lower > 0) types++;
if (digit > 0) types++;
return types >= 2 && special >= 1;
}
核心考点:字符遍历 + 多条件统计 + 规则验证。
五、易错点
1. 空字符串和单字符字符串(近场考试最高频陷阱)
空字符串和单字符字符串这两个极端场景,必须单独写代码验证,绝对不能让循环从 i=0 开始直接覆盖所有情况,这是近3场考试里 60% 考生丢分的高频陷阱。
// 错误写法:假设字符串长度至少为2
for (int i = 0; i < s.size() - 1; i++) { ... }
// 如果 s 为空,s.size() - 1 会变成很大的数(size_t 无符号),导致越界!
// 正确写法:先检查长度
if (s.empty()) { cout << "空字符串"; return 0; }
for (int i = 0; i + 1 < (int)s.size(); i++) { ... }
2. 下标越界
写代码前先在草稿纸上把字符串的每个字符标上从 0 开始的下标,确认遍历范围完全正确后再写循环,这类错误占编程题失分的 40% 以上。
string s = "GESP"; // 有效下标:0, 1, 2, 3
// s[4] 是越界访问!
3. size() 返回无符号整数
string s;
// ❌ 错误:s.size() 是 size_t,无符号整数,s.size() - 1 可能溢出
for (int i = 0; i < s.size() - 1; i++) { ... }
// ✅ 正确:强制转为 int
for (int i = 0; i + 1 < (int)s.size(); i++) { ... }
4. find 找不到时返回 string::npos
// ❌ 错误:不能用 -1 判断
if (s.find("abc") == -1) { ... }
// ✅ 正确:用 string::npos
if (s.find("abc") == string::npos) { ... }
5. cin >> s 遇到空格停止
string s;
cin >> s; // 输入 "Hello World",s 只得到 "Hello"
getline(cin, s); // 正确,读入整行 "Hello World"
6. substr 参数含义
string s = "GESP C++";
s.substr(5, 3); // 从下标5开始,取3个字符,返回 "C++"
s.substr(5); // 从下标5开始,取到末尾,返回 "C++"
第一个参数是起始下标,第二个参数是截取长度,不是结束下标。
7. 字符统计用数组提速
所有字符统计类题目,优先用 int cnt[256] 的数组直接计数,不用嵌套循环逐个比对,时间复杂度直接从 O(n²) 降到 O(n),彻底避免超时。
int cnt[256] = {0};
for (char c : s) {
cnt[(int)c]++;
}
// cnt['a'] 就是字符 'a' 出现的次数
六、速查表
| 需求 | 写法 | 说明 |
|---|---|---|
| 定义字符串 | string s = "Hello"; |
需 <string> |
| 读入一行 | getline(cin, s); |
含空格 |
| 长度 | s.size() 或 s.length() |
返回字符个数 |
| 判空 | s.empty() |
空返回 true |
| 访问字符 | s[i] 或 s.at(i) |
下标从0开始 |
| 首/末字符 | s.front() / s.back() |
|
| 查找 | s.find(sub, pos) |
找不到返回 string::npos |
| 截取子串 | s.substr(pos, len) |
省略 len 取到末尾 |
| 插入 | s.insert(pos, str) |
|
| 删除 | s.erase(pos, len) |
|
| 替换 | s.replace(pos, len, str) |
|
| 追加 | s.append(str) / s += str |
|
| 末尾加字符 | s.push_back(c) |
|
| 末尾删字符 | s.pop_back() |
|
| 清空 | s.clear() |
|
| 字符串转数字 | stoi(s) / stod(s) |
|
| 数字转字符串 | to_string(n) |
|
| 判断大写 | isupper(c) |
需 <cctype> |
| 判断小写 | islower(c) |
|
| 判断数字 | isdigit(c) |
|
| 转大写 | toupper(c) |
|
| 转小写 | tolower(c) |
七、总结
string 字符串及其函数是 GESP 三级考试的核心模块,分值占比 15%-20%。
string自动管理内存,比char数组更安全、更便捷。size()和length()功能相同,返回字符个数(不含结束符)。cin >> s遇到空格停止,读整行用getline(cin, s)。find找不到返回string::npos,不能用-1判断。substr(pos, len)第一个参数是起始下标,第二个是截取长度。insert插入、erase删除、replace替换,参数含义要记牢。- 遍历前先在草稿纸上标好下标,避免越界。
- 空字符串和单字符字符串必须单独验证,这是近场考试最高频陷阱。
size()返回无符号整数,与int比较时要强制转换。- 字符统计优先用
int cnt[256]数组,O(n) 效率不超时。
复习建议:先掌握 find、substr、getline 三个最常考的函数,再练习“字母求和”“密码合规检测”两类真题,最后挑战“分割字符串”这类综合题。每道题写完必须用空字符串、单字符字符串、含空格字符串三组边界数据手动验证。考试中先读懂题目规则,确定需要哪些字符串函数,再写遍历和判断逻辑,最后逐行检查下标和边界。