火龙信奥
  • 分享
  • 课程
  • 题库
  • 打卡
    • 代码对战
    • 快速对战
  • 题单
  • 团队
  • 荣誉墙
  • 商城
  • 登录 / 注册

第七章 C++ 字符串与文本处理讲义

作者: 作者的头像   huolong , 时间:2026-08-20 19:01:29 , 所有人可见, 阅读  37

在仔细阅读了您分享的关于字符串操作的笔记后,我为您整理了一份更系统、更具实用性的 C++ 字符串操作讲义。

在编写这份讲义时,我对您笔记中的一些细节进行了优化和补充,这些细节在实际编程和算法竞赛(如 CSP/NOIP)中非常容易成为“扣分陷阱”: 1. strcmp 的返回值:根据标准库定义,strcmp 只保证返回正数、负数或 0,并不一定会严格返回 1 或 -1。在代码中进行判断时,应使用 strcmp(a, b) > 0 而非 == 1。 2. fgets 读入换行符的问题:fgets 在读取一行时,会将末尾的换行符 \n 也存入字符串中,这在进行字符串匹配或长度计算时极易出错,通常需要手动将其滤掉。 3. string_view 的无零字符结尾(\0)陷阱:string_view 只是对已有字符串的视图,它所指向的区间并不保证以 \0 结尾。如果直接将其内部地址(.data())传给 printf("%s") 或 strlen,会导致越界读取。 4. 延续第 8 章:基于您大纲中的“8”,我顺承第 7 章的内容,为您补充了 “8. C++ 标准字符串类 std::string”,帮助您完成从 C 风格字符数组到现代 C++ 字符串的知识过渡。

以下是为您整理的讲义 Markdown 内容:


C++ 字符串与文本处理讲义

在 C++ 中,字符串的处理主要分为两套体系: 1. C 风格字符串:基于 char 数组和指针,以 \0 结尾。运行效率极高,空间占用紧凑,是算法竞赛(如 CSP/NOIP)中处理大规模输入输出的利器。 2. C++ 风格字符串 (std::string):面向对象的标准库类。安全易用,支持动态扩容,在现代 C++ 开发及日常编程中被广泛推荐。


7. C 风格字符串操作

7.1 核心机制与内存安全

C 风格字符串在内存中是一组连续的字符,且必须以空字符 '\0'(ASCII 码为 0)作为结束标记。 * 如果字符数组中没有 '\0',任何试图读取该字符串的库函数(如 strlen, strcpy, printf)都会继续向后读取相邻的内存,导致越界访问(Undefined Behavior),在竞赛中常表现为 RE(运行错误)或 WA(答案错误)。


7.2 字符串的输入与输出

A. 格式化读写(不含空格的字符串)

char str[100];
// 输入:遇到空格、回车或 TAB 会终止输入
scanf("%s", str); 
cin >> str;

// 输出
printf("%s\n", str);
cout << str << endl;

B. 整行读入(包含空格的字符串)

当字符串中包含空格时,需要使用整行读取函数。 在 C++ 中,严禁使用已被废弃且极具安全隐患的 gets(),应使用 fgets()。

// 语法:fgets(字符数组名, 最大读取长度, 输入流)
fgets(str, 100, stdin); 
  • 注意(陷阱):如果行长度小于最大读取长度,fgets 会将换行符 \n 一并读入并存储在字符串末尾。
  • 去除换行符的标准写法: cpp #include <cstring> // 将首个出现的 \n 替换为 \0 str[strcspn(str, "\n")] = '\0';

7.3 核心处理函数(头文件 <cstring>)

1. 获取长度:strlen(str)

返回字符串的实际字符个数,不包括末尾的 '\0'。该操作的时间复杂度为 $O(N)$,因此切忌在循环条件中重复调用(如 for(int i=0; i<strlen(str); i++) 会使循环复杂度退化为 $O(N^2)$)。

2. 字符串复制:strcpy 与安全版本 strncpy

// 将 str2 的内容(连同 '\0')复制到 str 中
strcpy(str, str2); 

// 安全版本:最多复制 n 个字符,防止 str 空间不足导致溢出
strncpy(str, str2, sizeof(str) - 1);
str[sizeof(str) - 1] = '\0'; // 必须手动确保末尾有 '\0'

3. 字符串拼接:strcat 与安全版本 strncat

将 str2 的内容连接到 str 的末尾(覆盖原 str 末尾的 \0),str 必须有足够的剩余空间。

strcat(str, str2);
strncat(str, str2, n); // 将 str2 的前 n 个字符连接到末尾

4. 字符串比较:strcmp 与 strncmp

逐个字符比较 ASCII 码。

int res = strcmp(str, str2);
  • 判断依据:
  • res > 0:表示 str 的字典序大于 str2(在实际环境中,返回值不一定是 1)。
  • res == 0:表示两字符串完全相等。
  • res < 0:表示 str 的字典序小于 str2(返回值不一定是 -1)。

5. 字符与子串查找:strchr 与 strstr

  • 查找字符:strchr(str, c) 返回指向字符 c 首次出现位置的指针;若未找到,返回 nullptr。
  • 查找子串:strstr(str, str2) 返回指向子串 str2 首次出现位置的指针;若未找到,返回 nullptr。

计算具体索引(下标)的方法:利用指针减法。

char* pos = strstr(str, "target");
if (pos != nullptr) {
    int index = pos - str; // 利用指针相减获取在数组中的 0 基索引
    printf("Found at index: %d\n", index);
}

7.4 格式化与数值转换

在算法竞赛和工程开发中,常常需要在字符串与数值之间进行高效率的转换。

A. 内存格式化读写(sscanf 与 sprintf)

  • sscanf:从字符串中读取数据(类似于 scanf,只是输入源改为了字符串)。
  • sprintf:向字符串中写入格式化内容(常用于数值转字符串、复杂格式拼接)。
char buffer[100];
int age = 20;
double score = 95.5;

// 数值格式化为字符串
sprintf(buffer, "Age:%d Score:%.1f", age, score); 
// buffer 变为 "Age:20 Score:95.5"

// 从字符串中解析数据
int parsed_age;
double parsed_score;
sscanf(buffer, "Age:%d Score:%lf", &parsed_age, &parsed_score);

B. 现代 C++ 数值转换(to_string)

头文件 <string>。可以直接将各种数值类型转化为 std::string。

#include <string>
std::string s1 = std::to_string(123);       // s1 = "123"
std::string s2 = std::to_string(3.14159);  // s2 = "3.141590" (默认保留 6 位小数)

7.5 现代 C++ 零拷贝视图:std::string_view (C++17)

头文件 <string_view>。 * 原理:它是一个轻量级的、非所有权的只读字符串视图(仅包含一个指向字符数据的指针和长度)。 * 优点:在函数传参时,相比 const std::string&,它可以完美兼容 C 风格字符串和 std::string,且完全避免了任何内存拷贝。

#include <string_view>
#include <iostream>

// 使用 string_view 作为参数,既高效又通用
void printWithLength(std::string_view sv) {
    std::cout << sv << " (length: " << sv.length() << ")\n";
}

int main() {
    printWithLength("Hello World"); // 传入 C 风格字符串,无拷贝
    std::string s = "C++";
    printWithLength(s);             // 传入 std::string,无拷贝
}

⚠️ 重要警告(安全隐患): std::string_view 不保证其内部字符以 \0 结尾(例如取子串 sv.substr(0, 3) 时,底层并不会被截断填入 \0)。因此,绝不能直接将 sv.data() 传给 printf("%s") 或 strlen(),否则会发生内存越界。


8. C++ 标准字符串类 std::string

作为 C++ 标准库(STL)中最常用的容器之一,std::string(头文件 <string>)彻底解决了 C 风格字符串内存越界和手动管理空间的烦恼。

8.1 基本定义与常规操作

  • 声明与初始化: cpp #include <string> std::string s1 = "Hello"; std::string s2("World");
  • 拼接与比较:支持直接使用运算符 +、+=、==、<、>。 cpp std::string s3 = s1 + " " + s2; // s3 = "Hello World" if (s1 < s2) { /* 字典序比较 */ }
  • 长度与空判断: cpp s1.length(); // 或 s1.size(),时间复杂度为 O(1) s1.empty(); // 返回 bool 值,判断是否为空

8.2 核心成员函数

std::string 提供了极为丰富的成员函数,大大降低了字符串处理的逻辑难度。

1. 子串截取:substr

// 语法:s.substr(起始位置, 截取长度)
std::string s = "Hello C++ World";
std::string sub = s.substr(6, 3); // sub = "C++"

2. 查找子串与字符:find

std::string s = "Hello C++";
size_t pos = s.find("C++"); // 返回找到的第一个字符的索引

if (pos != std::string::npos) {
    // 找到了,pos = 6
} else {
    // 未找到,返回值为 std::string::npos
}

3. 插入与删除:insert 与 erase

std::string s = "Hello World";
s.insert(5, " C++"); // 在索引 5 处插入,s 变为 "Hello C++ World"

s.erase(5, 4);       // 从索引 5 开始,删除 4 个字符,s 变回 "Hello World"

4. 替换子串:replace

std::string s = "I love Python";
s.replace(7, 6, "C++"); // 从第 7 个字符开始,将 6 个字符长度的子串替换为 "C++"
// s 变为 "I love C++"

8.3 字符属性判定函数(头文件 <cctype>)

无论是 C 风格还是 C++ 风格字符串,遍历其中的每一个字符时,通常需要判断其是否为数字、字母等。<cctype> 库提供了非常高效的辅助函数:

函数 功能描述
isdigit(c) 判断字符 c 是否为十进制数字('0' ~ '9')
isalpha(c) 判断字符 c 是否为英文字母('a'~'z', 'A'~'Z')
isalnum(c) 判断字符 c 是否为字母或数字
islower(c) 判断字符 c 是否为小写字母
isupper(c) 判断字符 c 是否为大写字母
isspace(c) 判断字符 c 是否为空格、制表符、换行符等空白字符
tolower(c) 将字符 c 转换为小写(非大写字母则保持原样)
toupper(c) 将字符 c 转换为大写(非小写字母则保持原样)

示例应用:统计一个字符串中数字和英文字母的个数:

#include <iostream>
#include <string>
#include <cctype>

int main() {
    std::string s = "2026 Happy New Year!";
    int digit_cnt = 0, alpha_cnt = 0;

    for (char c : s) {
        if (std::isdigit(c)) digit_cnt++;
        else if (std::isalpha(c)) alpha_cnt++;
    }

    std::cout << "Digits: " << digit_cnt << "\n"; // 输出 4
    std::cout << "Letters: " << alpha_cnt << "\n"; // 输出 12
    return 0;
}

—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com

关于火龙

  • 关于我们
  • 学员获奖
  • 预约试听
  • ACM课程
  • CSP课程
  • 学习指南

帮助中心

  • 用户协议
  • 打字练习
  • 在线画图
  • DevC++下载
  • CSP报名
  • GESP官网

推荐课程

  • C++零基础入门(可试看)
  • C++进阶提升
  • GESP考级辅导
  • GESP打卡
  • CSP-J/S打卡

公众号

火龙信奥公众号二维码

地址:义乌市北门街188号新天地商厦二楼2F 邮箱:wdlok305@126.com

© 2017-2026 义乌市睿码科技有限公司版权所有 浙ICP备2021013995号

火龙信奥
请输入登录信息


请完成安全验证
验证码底图 滑块
向右拖动滑块完成验证
请输入用户名 / 绑定的手机号码



请输入注册信息(手机号验证码注册)





验证码5分钟有效,60秒内不可重复获取,每日最多3次

微信登录

微信登录二维码

正在生成二维码...

账号已过期,请续期。
去续期

绑定手机号

📱

为了更好地保护您的账号安全,享受完整的平台服务

请您尽快绑定手机号码