- juxq 的博客
字符串教学讲义
- @ 2026-4-4 11:42:09
字符串教学讲义
1. 字符串简介
1.1 什么是字符串
字符串(String):由零个或多个字符组成的有限序列。在 C++ 中,字符串可以用字符数组(
char[])或标准库string类来表示。
单个字符用 char 类型存储,例如 'A'、'9'、'#'。而字符串则是多个字符依次排列,并以结束符 \0(空字符)标记结尾。
图示:字符串在内存中的存储
char arr[] = "hello";
内存布局:
地址 → [0] [1] [2] [3] [4] [5]
内容 → h e l l o \0
索引(下标) 0 1 2 3 4 5 (\0 不计入有效长度)
有效长度 = 5,存储空间 = 6(包含 \0)
1.2 为什么需要字符串
- 文本处理:姓名、地址、题目描述、用户输入等都需要字符串。
- 信息学竞赛:字符串处理(如回文、子串、匹配、加密)是高频考点。
- 输入输出:文件读写、格式化输出常与字符串打交道。
2. C++ 中字符串的三种形式
2.1 字符 char
- 单个字符,用单引号括起。
- 本质是 ASCII 码(0~127 或 0~255 的整数),可参与算术运算。
常用函数(需包含 <cctype> 或 <bits/stdc++.h>):
| 函数 | 作用 | 示例 |
|---|---|---|
isupper(ch) |
是否为大写字母 | isupper('A') → true |
islower(ch) |
是否为小写字母 | islower('a') → true |
isalpha(ch) |
是否为字母 | isalpha('Z') → true |
isdigit(ch) |
是否为数字 | isdigit('3') → true |
isalnum(ch) |
是否为字母或数字 | isalnum('a') → true |
toupper(ch) |
转换为大写(返回整型,需强转) | toupper('b') → 'B' |
tolower(ch) |
转换为小写 | tolower('G') → 'g' |
代码示例:
#include <bits/stdc++.h>
using namespace std;
int main() {
char ch = 'A';
cout << isupper(ch) << endl; // 1
cout << islower(ch) << endl; // 0
cout << isalpha(ch) << endl; // 1
cout << isdigit(ch) << endl; // 0
cout << isalnum(ch) << endl; // 1
cout << char(toupper('b')) << endl; // 'B'
cout << char(tolower('G')) << endl; // 'g'
return 0;
}
2.2 字符数组 char[]
- 固定大小的连续内存,以
\0结尾。 - 定义时必须指定长度(或自动推断)。
常用操作:
| 操作 | 函数/方式 | 说明 |
|---|---|---|
| 输入(不含空格) | cin >> arr; |
遇空白停止 |
| 输入整行(含空格) | cin.getline(arr, sizeof(arr)); |
读取一行,最多 size-1 个字符 |
| 输出 | cout << arr; |
输出直到 \0 |
| 获取定义长度 | sizeof(arr) |
编译时确定的字节数 |
| 获取有效长度 | strlen(arr) |
从开头到 \0 的字符数 |
| 复制 | strcpy(dest, src); |
将 src 复制到 dest |
| 拼接 | strcat(dest, src); |
将 src 连接到 dest 末尾 |
| 比较 | strcmp(s1, s2); |
返回 0(相等)、正(s1>s2)、负 |
代码示例:
#include <bits/stdc++.h>
using namespace std;
int main() {
char arr[100] = "hello world";
cin >> arr; // 输入 "abc" -> arr="abc"
cin.getline(arr, 100); // 读取一行(会吸收之前的换行符,注意配合 cin.ignore())
cout << arr << endl;
cout << sizeof(arr) << endl; // 100(字节数)
cout << strlen(arr) << endl; // 实际字符个数(不含 \0)
char arr1[100] = "hello";
char arr2[100] = "world";
strcpy(arr1, arr2); // arr1 变为 "world"
strcat(arr1, arr2); // arr1 变为 "worldworld"
cout << strcmp(arr1, arr2) << endl; // 比较结果
return 0;
}
注意:字符数组操作容易导致缓冲区溢出(目标空间不足),推荐使用
string。
2.3 string 类(标准库)
- 可变长度,自动管理内存,提供丰富的成员函数。
- 需要包含
<string>(<bits/stdc++.h>已包含)。
构造与赋值:
string s1; // 空字符串
string s2 = "hello"; // 直接初始化
string s3(10, 'x'); // 10 个 'x': "xxxxxxxxxx"
string s4(s2); // 拷贝构造
s1 = s2; // 赋值
输入输出:
string s;
cin >> s; // 遇空白停止
getline(cin, s); // 读取整行(包含空格)
cout << s << endl;
常用成员函数:
| 函数/操作 | 示例 | 说明 |
|---|---|---|
size() / length() |
s.size() |
返回字符串长度 |
empty() |
s.empty() |
是否为空 |
clear() |
s.clear() |
清空字符串 |
push_back(ch) |
s.push_back('A') |
末尾添加字符 |
append(str) / += |
s += "def" |
字符串拼接 |
insert(pos, str) |
s.insert(2, "abc") |
在位置 2 插入子串 |
erase(pos, len) |
s.erase(2, 3) |
从位置 2 删除 3 个字符 |
substr(pos, len) |
s.substr(2, 3) |
提取子串(从 2 开始,长度 3) |
find(str) |
s.find("llo") |
查找子串,返回首下标(失败返回 npos) |
replace(pos, len, str) |
s.replace(2, 3, "xyz") |
替换子串 |
c_str() |
s.c_str() |
返回 const char*(用于 C 函数) |
代码示例:
#include <bits/stdc++.h>
using namespace std;
int main() {
string s = "hello world";
cout << s.size() << " " << s.length() << endl; // 11 11
s.push_back('!');
s += "!!!";
cout << s << endl; // hello world!!!!
s.insert(5, " dear");
cout << s << endl; // hello dear world!!!!
cout << s.substr(6, 4) << endl; // dear
s.replace(6, 4, "beautiful");
cout << s << endl; // hello beautiful world!!!!
auto pos = s.find("world");
if (pos != string::npos)
cout << "found at " << pos << endl;
s.erase(5, 10); // 删除 " beautiful "
cout << s << endl; // hello world!!!!
s.clear();
cout << (s.empty() ? "empty" : "not empty") << endl;
return 0;
}
迭代器与范围 for:
string s = "hello";
for (char c : s) {
cout << c << " "; // h e l l o
}
// 使用迭代器
for (auto it = s.begin(); it != s.end(); ++it) {
*it = toupper(*it);
}
cout << s; // HELLO
数值与字符串互转:
// 数值 → 字符串
int n = 123;
string s1 = to_string(n); // "123"
double d = 3.14;
string s2 = to_string(d); // "3.140000"
// 字符串 → 数值
string s = "123456";
int x = stoi(s); // 123456
long long y = stoll(s);
double z = stod("123.456"); // 123.456
字符数组与 string 互转:
// char[] → string
char arr[100] = "hello";
string s = arr; // 直接赋值
// string → char[]
string s = "world";
char arr[100];
strcpy(arr, s.c_str()); // 注意 c_str() 返回 const char*
3. 字符串的常见操作图示
查找子串:
s = "hello world"
012345678910
s.find("world") → 6
s.find("abc") → string::npos (通常为 -1)
提取子串:
s = "hello world"
s.substr(2, 3) → "llo" (从下标2开始,取3个字符)
插入与删除:
原串: "hello world"
s.insert(5, " dear") → "hello dear world"
s.erase(5, 5) → "hello world"(删除 " dear")
4. 课堂例题
例题1:统计字母、数字、空格个数
题目描述
输入一行字符串(可能包含空格),统计其中大写字母、小写字母、数字、空格以及其他字符的个数。
输入:一行字符串(长度 ≤ 1000)。 输出:四个整数,依次为大写字母数、小写字母数、数字数、空格数、其他字符数。
思路解析
使用 getline 读取整行字符串。遍历每个字符,利用 <cctype> 函数判断类型,分别累加。注意:isalpha 不区分大小写,但我们需要分别统计大写和小写,可用 isupper 和 islower。
代码实现
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
getline(cin, s); // 读取整行
int upper = 0, lower = 0, digit = 0, space = 0, other = 0;
for (char ch : s) {
if (isupper(ch)) upper++;
else if (islower(ch)) lower++;
else if (isdigit(ch)) digit++;
else if (ch == ' ') space++;
else other++;
}
cout << upper << " " << lower << " " << digit << " " << space << " " << other << endl;
return 0;
}
例题2:字符串反转与回文判断
题目描述
输入一个字符串(不含空格),判断它是否是回文(正读反读相同)。若是,输出
"yes",否则输出"no"。输入:一行字符串(长度 ≤ 1000)。 输出:
"yes"或"no"。
思路解析
方法一:直接比较第 i 个字符与倒数第 i 个字符是否相等,只需遍历前一半。 方法二:将字符串反转后与原串比较。
注意:空字符串和单个字符视为回文。
代码实现
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
bool ok = true;
int len = s.size();
for (int i = 0; i < len / 2; i++) {
if (s[i] != s[len - 1 - i]) {
ok = false;
break;
}
}
cout << (ok ? "yes" : "no") << endl;
return 0;
}
例题3:字符串加密(凯撒密码)
题目描述
输入一个由大写字母组成的字符串,将其每个字母向后移动 3 位(即 A→D, B→E, …, Z→C),输出加密后的字符串。
输入:一行大写字母字符串(长度 ≤ 1000)。 输出:加密后的字符串。
思路解析
遍历每个字符,将其 ASCII 码加 3。若超过 'Z',则回绕到 'A'。公式:newChar = (ch - 'A' + 3) % 26 + 'A'。
代码实现
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
for (char& ch : s) {
if (isupper(ch)) {
ch = (ch - 'A' + 3) % 26 + 'A';
}
}
cout << s << endl;
return 0;
}
5. 课后练习
以下题目在洛谷(luogu.com.cn)上搜索题号即可。
| 序号 | 题目名称 | 题号 | 知识点 |
|---|---|---|---|
| 1 | 【深基5.例2】输出字符三角形 | P5738 | 字符串输出、循环 |
| 2 | 【深基5.例3】数字反转 | P5739 | 字符串反转 |
| 3 | 【深基5.例4】统计单词数 | P1308 | 字符串查找、大小写转换 |
| 4 | 【深基5.例5】自动修正 | P5735 | 字符替换 |
| 5 | 书名号 | P1957 | 字符串拼接、数值转换 |
| 6 | 手机号 | P1765 | 字符映射(模拟) |
| 7 | 拼数 | P1012 | 字符串排序(自定义比较) |
| 8 | 删除单词后缀 | P1415 | 子串判断、删除 |
| 9 | 子串查找 | P3375 | KMP(提高)或简单查找 |
| 10 | 单词反转 | P1553 | 字符串分割、反转 |
建议:先独立完成基础题(1~6),再挑战排序和模式匹配。
6. 总结
| 形式 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
char |
简单,直接处理 ASCII | 单个字符,无法表示多个字符 | 字符判断、转换 |
字符数组 char[] |
兼容 C 语言,适合底层操作 | 长度固定,易溢出,函数繁琐 | 极少数对性能要求极高的场合 |
string |
安全、方便、功能丰富 | 略慢于字符数组(可忽略) | 日常竞赛首选 |
核心技巧:
- 输入带空格的字符串:用
getline(cin, s)。 - 判断字符类型:优先使用
<cctype>函数,而不是手写范围。 - 字符串查找:
find()返回string::npos表示未找到。 - 字符串转数字:
stoi()/stoll()/stod();数字转字符串:to_string()。 - 频繁修改字符串(插入、删除)时,
string效率依然很高。
教练寄语:字符串处理是信息学竞赛的基础技能。请务必熟练使用
string的各种成员函数,并养成用cctype函数判断字符类型的习惯。课后的 10 道练习题涵盖了常见考点,认真完成它们,你将能应对大部分字符串问题。
参考资料
- 【书籍】《信息学奥赛一本通》第4章:字符串
- 【书籍】《C++ Primer Plus》第16章:string 类和标准模板库
- 【在线资源】cppreference - std::string