逆 天!从 STL 到 string,这 波 字 符 串 神 操 作 直 接 让 编 译 器 跪 了 - - - 程 序 员 必 学 的 开 挂 级 技 巧
逆 天!从 STL 到 string,这 波 字 符 串 神 操 作 直 接 让 编 译 器 跪 了 - - - 程 序 员 必 学 的 开 挂 级 技 巧
💻作 者 简 介:曾 与 你 一 样 迷 茫,现 以 经 验 助 你 入 门 C++。
💡个 人 主 页:@笑口常开xpr 的 个 人 主 页
📚系 列 专 栏:C++ 炼 魂 场:从 青 铜 到 王 者 的 进 阶 之 路
✨代 码 趣 语:迭 代 器 就 像 地 铁 检 票 员,从 起 点(begin)到 终 点(end),一 个 一 个 地 检 查(遍 历)乘 客(字 符)!
💪代 码 千 行,始 于 坚 持,每 日 敲 码,进 阶 编 程 之 路。
📦gitee 链 接:gitee
在 C++ 编 程 中,对 字 符 串 的 处 理 是 极 为 常 见 的 需 求。STL(标 准 模 板 库)中 的 string 类 作 为 专 门 用 于 字 符 串 管 理 的 工 具,封 装 了 丰 富 的 功 能,能 高 效 实 现 字 符 串 的 创 建、遍 历、增 删 查 改 等 操 作。本 文 将 从 STL 的 基 础 概 念 入 手,深 入 剖 析 string 类 的 使 用 细 节,助 力 开 发 者 熟 练 掌 握 这 一 实 用 工 具。
STL
定 义
STL(standard template libaray),即 标 准 模 版 库 的 一 部 分,I O 流 不 包 含 STL。STL 是 C++ 标 准 库 的 重 要 组 成 部 分,不 仅 是 一 个 可 复 用 的 组 件 库,而 且 是 一 个 包 含 数 据 结 构 与 算 法 的 软 件 框 架。
版 本
原 始 版 本
Alexander Stepanov、Meng Lee 在 惠 普 实 验 室 完 成 的 原 始 版 本,本着 开 源 精 神,他 们 声 明 允 许 任 何 人 任 意 运 用、拷 贝、修 改、传 播、商 业 使 用 这 些 代 码,无 需 付 费。唯 一 的 条 件 就 是 也 需 要 向 原 始 版 本 一 样 做 开 源 使 用。 HP 版 本 - - - 所 有 STL 实 现 版 本 的 始 祖。
P. J. 版 本
由 P. J. Plauger 开 发,继 承 自 HP 版 本,被 Windows Visual C++ 采 用,不 能 公 开 或 修 改。
缺 陷:可 读 性 比 较 低,符 号 命 名 比 较 怪 异。
RW 版 本
由 Rouge Wage 公 司 开 发,继 承 自 HP 版 本,被 C+ + Builder 采 用,不 能 公 开 或 修 改,可 读 性 一 般。
SGI 版 本
由 Silicon Graphics Computer Systems,Inc 公 司 开 发,继 承 自 HP 版 本。被 GCC(Linux) 采 用,可 移 植 性 好,可 公 开、修 改 甚 至 贩 卖,从 命 名 风 格 和 编 程 风 格 上 看,阅 读 性 非 常 高。
六 大 组 件
- 容 器
容 器 是 用 于 存 储 和 管 理 数 据 的 类 模 板,也 就 是 数 据 结 构,分 为 顺 序 容 器、关 联 容 器 和 容 器 适 配 器。
顺 序 容 器
vector:动 态 数 组,支 持 随 机 访 问,尾 部 插 入 / 删 除 效 率 高。
deque:双 端 队 列,支 持 头 尾 高 效 插 入 / 删 除。
list:双 向 链 表,支 持 任 意 位 置 高 效 插 入 / 删 除。
关 联 容 器
set/multiset:基 于 红 黑 树 实 现,元 素 自 动 排 序,set 不 允 许 重 复,multiset 允 许。
map/multimap:键 值 对 存 储,基 于 红 黑 树,map 键 唯 一,multimap 键 可 重 复。
- 算 法
STL 提 供 了 超 过 100 种 算 法,涵 盖 排 序、查 找、遍 历、修 改 等 操 作,通 过 迭 代 器 与 容 器 解 耦。 - 迭 代 器
迭 代 器 是 一 种 抽 象 的 指 针,用 于 遍 历 容 器 中 的 元 素,提 供 统 一 的 访 问 接 口。 - 仿 函 数
仿 函 数(也 称 函 数 对 象)是 重 载 了 operator() 的 类 或 结 构 体,可 作 为 算 法 的 参 数。 - 配 接 器
配 接 器 是 一 种 特 殊 的 函 数 对 象 或 容 器,用 于 修 改 其 他 组 件 的 接 口。
空 间 配 置 器
空 间 配 置 器 是 负 责 管 理 容 器 内 存 的 组 件,允 许 自 定 义 内 存 分 配 策 略。


string 库
定 义
string 是 C++ 标 准 库(std)中 用 于 处 理 字 符 串 的 类,封 装 了 字 符 串 的 存 储、操 作 等 功 能,string 类 中 含 有 字 符 串 的 增 删 查 改 和 算 法,用 来 管 理 字 符 数 组。类 似 的,string 不 带 .h。
构 造 函 数

上 面 的 构 造 函 数 3、5、7 需 要 认 识,其 余 的 需 要 重 点 掌 握。
常 用 的 定 义 方 式
#include<iostream> #include<string> using namespace std; int main(){ string s1;//string(); string s2("hello world");//string(const char* s); string s3(10,'*');//n个字符初始化,string (size_t n, char c); string s4(s2);//拷贝构造,string (const string& str);return0;}这 些 是 经 常 要 用 到 的 定 义 方 式,如 果 遇 到 不 会 的,建 议 读 者 多查 文 档。
拷 贝 字 符 串 的 一 部 分


#include<iostream> #include<string> using namespace std; int main(){ string s1("hello world");//string(const char* s); string s2(s2,6,5);//string (const string& str, size_t pos, size_t len = npos); cout << s2 << endl;return0;}

npos 是 缺 省 参 数,也 是 string 类 中 的 静 态 成 员 变 量,它 的 值 是 -1,即 默 认 的 是 整 形 的 最 大 值。如 果 npos 的 值 比 较 大 或 者 没 有 ,则 字 符 串 取 到 结 尾。
比 较 字 符 串
#include<iostream> #include<string> using namespace std; int main(){ string s1; string s2("张三");//比较 cout <<(s1 == s2)<< endl; cout <<(s1 < s2)<< endl; cout <<(s1 > s2)<< endl;return0;}
0 表 示 false,即 条 件 不 成 立,1 表 示 true,即 条 件 成 立。
比 较 时 要 带 上 括 号,原 因 是 流 插 入 运 算 符 的 优 先 级 比 比 较 运 算 符 优 先 级 高。
字 符 串 分 割

#include<iostream> #include<string> using namespace std; int main(){ string url("https://legacy.cplusplus.com/reference"); string sub1(url,0,5); string sub2(url,8,20); string sub3(url,29, url.size()-29); cout << sub1 << endl; cout << sub2 << endl; cout << sub3 << endl;return0;}

sub1 从 索 引 0 开 始 提 取 5 个 字 符。
sub2 从 索 引 8 开 始(跳 过 https://),提 取 20 个 字 符。
sub3 从 索 引 29 开 始,提 取 到 字 符 串 末 尾(url.size() 是 字 符 串 的 长 度, 减 去 前 面 的 所 有 字 符 个 数)。

这 三 个 运 算 符 支 持 运 算 符 重 载 并 且 构 成 了 函 数 重 载。
#include<iostream> #include<string> using namespace std; int main(){ string s1; string s2("张三");//operator= s1 = s2;//string& operator=(const string & str);拷贝构造 cout << s1 << endl; s1 ="1111";//string& operator=(const char* s);常量字符串 cout << s1 << endl; s1 ='*';//string& operator=(char c);//字符 cout << s1 << endl;return0;}
析 构 函 数

对 象 出 了 作 用 域 会 自 动 调 用 析 构 函 数,我 们 不 用 写,编 译 器 会 自 动 生 成。
迭 代 器



迭 代 器 是 像 指 针 一 样 的 类 型,迭 代 器 不 能 修 改。
任 何 容 器 都 支 持 迭 代 器,并 且 用 法 是 类 似 的。
#include<iostream> #include<string> using namespace std; int main(){ string s1("hello world"); string::iterator it = s1.begin();while(it != s1.end()){ cout <<*it <<" "; it++;} cout << endl;return0;}
begin 和 end 是 左 闭 右 开 的 关 系,即 [begin, end)。

应 用 场 景
读 和 写
#include<iostream> #include<string> using namespace std; int main(){//begin和end是左闭右开的关系//[begin, end) string s1("hello world"); string::iterator it = s1.begin();//写while(it != s1.end()){(*it)--; it++;} it = s1.begin();//读while(it != s1.end()){ cout <<*it <<" "; it++;} cout << endl;return0;}
范 围 for
底 层 是 迭 代 器,范 围 for 只 能 正 着 遍 历,不 能 倒 着 遍 历。
#include <iostream> #include <string> using namespace std; int main(){ string s1("hello world");for(auto& ch : s1){ ch++;}for(auto ch : s1){ cout << ch <<" ";} cout << endl;return0;}
逆 置
reverse 是 一 个 函 数 模 板。迭 代 器 可 以 跟 容 器 配 合。
#include <iostream> #include <string> using namespace std; int main(){ string s1("hello world");for(auto& ch : s1){ ch++;}for(auto ch : s1){ cout << ch <<" ";} cout << endl;reverse(s1.begin(), s1.end());//逆置for(auto ch : s1){ cout << ch <<" ";} cout << endl;return0;}
反 向 迭 代 器
#include<iostream> #include<string> using namespace std; int main(){ string s1("hello world");//string::reverse_iterator rit = s1.rbegin(); auto rit = s1.rbegin();while(rit != s1.rend()){ cout <<*rit <<" "; rit++;} cout << endl;return0;}
const 迭 代 器
当 函 数 传 参 时 使 用 const 应 该 使 用 const_iterator。
普 通 迭 代 器 可 以 读 和 写,const 迭 代 器 只 能 读。
类 似 的,反 向 迭 代 器 使 用 const 也 需 要 使 用 const 迭 代 器。


方 法 1
使 用 const
#include<iostream> #include<string> using namespace std;voidFunc(const string& s)//不加引用需要深拷贝,代价较大{ string::const_iterator it = s.begin();while(it != s.end()){ cout <<*it <<" "; it++;}} int main(){ string s1("hello world");Func(s1);return0;}
方 法 2
传 参 没 有 使 用 const,则 可 以 使 用 const 也 可 以 不 使 用 const 迭 代 器。这 里 是 权 限 的 缩 小。
#include<iostream> #include<string> using namespace std;voidFunc(string& s)//不加引用需要深拷贝,代价较大{ string::const_iterator it = s.begin();while(it != s.end()){ cout <<*it <<" "; it++;}} int main(){ string s1("hello world");Func(s1);return0;}
总 结
iterator 提 供 一 种 统 一 的 方 式 访 问 和 修 改 容 器 中 的 数 据,算 法 就 可 以 通 过 迭 代 器,去 处 理 容 器 中 的 数 据。
容 量

size
size 用 来 求 出 字 符 串 长 度,不 包 括 ‘\0’。
\0 是 表 示 字 符 串 的 特 殊 字 符 并 且 不 会 被 打 印 出 来。


#include<iostream> #include<string> using namespace std; int main(){ string s1("hello world"); cout << s1.size()<< endl;//size是数据个数 cout << s1.length()<< endl;//length是长度return0;}
size 和 lenth 的 结 果 相 同,但 在 string 里 面 建 议 使 用 size。
max_size
字 符 串 可 以 达 到 的 最 大 长 度,不 同 编 译 器 的 结 果 是 不 一 样 的。

#include<iostream> #include<string> using namespace std; int main(){ string s1("hello world"); cout << s1.max_size()<< endl;return0;}

返 回 当 前 为 字 符 串 分 配 的 存 储 空 间 的 大 小,以 字 节 表 示。在 不 同 版 本 下 扩 容 的 结 果 是 不 一 样 的。
#include<iostream> #include<string> using namespace std; int main(){ string s1("hello world"); cout << s1.capacity()<< endl; size_t old = s1.capacity();for(size_t i =0; i <100; i++){ s1 +='x';if(old != s1.capacity()){ cout <<"扩容"<< s1.capacity()<< endl; old = s1.capacity();}}return0;}

clear 可 以 清 除 字 符 串 的 内 容。
#include<iostream> #include<string> using namespace std; int main(){ string s1("hello world"); cout << s1 << endl; cout << s1.size()<< endl; cout << s1.capacity()<< endl; s1.clear(); cout << s1.size()<< endl; cout << s1.capacity()<< endl; cout << s1 << endl;return0;}
clear 可 以 清 除 size 的 数 据,无 法 清 除 capacity 的 数 据。

开 辟 已 知 的 空 间,可 能 会 开 辟 更 大 的 空 间,但 不 会 比 这 个 空 间 小,不 会 扩 容。
#include<iostream> using namespace std; int main(){ string s1("hello world"); s1.reserve(100); size_t old = s1.capacity();for(size_t i =0; i <100; i++){ s1 +='x';if(old != s1.capacity()){ cout <<"扩容"<< s1.capacity()<< endl; old = s1.capacity();}}return0;}
注 意
如 果 开 辟 的 空 间 没 有 存 储 数 据,可 以 缩 小 空 间,缩 小 空 间 是 将剩 余 的 数 据 拷 贝 到 另 一 份 空 间,然 后 释 放 掉 原 来 所 有 的 空 间,它 是 以 时 间 换 空 间 的 做 法。
#include<iostream> #include<string> using namespace std; int main(){ string s1("hello world"); s1.reserve(100); size_t old = s1.capacity();for(size_t i =0; i <100; i++){ s1 +='x';if(old != s1.capacity()){ cout <<"扩容"<< s1.capacity()<< endl; old = s1.capacity();}}//有数据 cout << s1.capacity()<< endl; s1.reserve(10); old = s1.capacity(); cout << s1.capacity()<< endl;//无数据 s1.clear(); cout << s1.capacity()<< endl; s1.reserve(10); old = s1.capacity(); cout << s1.capacity()<< endl;return0;}

resize 可 以 开 空 间 和 初 始 化 填 充 数 据。
#include<iostream> #include<string> using namespace std; int main(){ string s1("hello world");//开空间 s1.reserve(100); cout << s1.size()<< endl; cout << s1.capacity()<< endl;//开空间+初始化填数据 s1.resize(100,'x'); cout << s1.size()<< endl; cout << s1.capacity()<< endl;return0;}


访 问 数 据


[ ] 本 质 上 是 解 引 用,是 数 组 用 来 访 问 元 素 的。
遍 历 字 符 串
方 法 1
#include<iostream> using namespace std; int main(){ string s1; string s2("hello world"); cout << s2 << endl;return0;}
方 法 2
#include<iostream> using namespace std; int main(){ string s1; string s2("hello world");//下标+[]//cout << s2.size() << endl; int i =0;for(i =0; i < s2.size(); i++){ s2[i]++;}for(i =0; i < s2.size(); i++){ cout << s2[i];} cout << endl;return0;}
区 分
#include<iostream> using namespace std; int main(){ string s2("hello world"); char s3[]="hello world"; s2[1];//s1.operator[](1); s3[1];//->*(s3 + 1)return0;} s2[1] 是 C++ string 类 的 重 载 运 算 符 调 用,实 际 执 行(s2.operator[ ] (1))。
s3[1] 是 数 组 访 问,等 价 于 指 针 运 算 *(s3 + 1),其 中 s3 退 化 为 指 向 首 元 素 的 指 针。

修 改 字 符 串 的 元 素,如 果 越 界 则 会 抛 出 异 常。
#include<iostream> #include<string> using namespace std; int main(){ string s1("hello world"); s1.at(0)='x'; cout << s1 << endl;return0;}
注 意
operator[ ] 越 界 会 断 言,at 越 界 是 抛 出 异 常。
at 越 界
#include<iostream> #include<string> using namespace std; int main(){ string s1("hello world"); s1.at(15)='x'; cout << s1 << endl;return0;}
operator[ ] 越 界
#include<iostream> #include<string> using namespace std; int main(){ string s1("hello world"); s1[15]='x';//operator[ ]越界 cout << s1 << endl;return0;}
增 删 查 改


#include<iostream> using namespace std; int main(){//增 string s1("hello"); s1 +=' '; s1 +="world"; cout << s1 << endl;return0;}
operator+= 字 符 调 用 push_back,operator+= 字 符 串 调 用 append。
将 x 转 成 string 对 象
#include<iostream> using namespace std; int main(){ int x =0; cin >> x; string xstr;while(x){ size_t val = x %10; xstr +=('0'+ val); x /=10;}//逆置return0;}
push_back 可 以 在 字 符 串 后 面 插 入 一 个 字 符。

append 可 以 在 字 符 串 后 面 插 入 一 个 字 符 串。
如 果 空 间 不 够,会 自 动 扩 容。
代 码 示 例
#include<iostream> using namespace std; int main(){//增 string s1("hello");//尾插一个字符 s1.push_back(' ');//尾插一个字符串 s1.append("world"); cout << s1 << endl;return0;}

append 是 在 字 符 串 后 面 追 加,assign 是 覆 盖 掉 原 来 的 字 符 串。赋 值 也 可 以 覆 盖 掉 原 来 的 字 符 串。
#include<iostream> #include<string> using namespace std; int main(){ string s1("hello ");//append追加 s1.append("world"); cout << s1 << endl;//覆盖 s1.assign("111111111"); cout << s1 << endl;return0;}

在 字 符 串 的 任 何 位 置 插 入 字 符。
#include<iostream> using namespace std; int main(){ string s1("world"); s1.insert(0,"hello "); cout << s1 << endl; s1.insert(s1.begin()+3,10,'z'); cout << s1 << endl;return0;}

删 除 字 符 串 某 一 个 位 置 的 字 符。
#include<string> #include<iostream> using namespace std; int main(){ string s1("hello world"); s1.erase(5,1); cout << s1 << endl; s1.erase(5); cout << s1 << endl;return0;}

#include<string> #include<iostream> using namespace std; int main(){ string s1("hello world"); s1.replace(6,5,"xxxxxxxxxx"); cout << s1 << endl;return0;}
所 有 的 空 格 替 换 为 20%
#include<string> #include<iostream> using namespace std; int main(){ string s2("hello world hello world"); string s3;for(auto ch : s2){if(ch ==' '){ s3 +="20%";}else{ s3 += ch;}} cout << s3 << endl;return0;}
查 找


从 前 向 后 找,返 回 字 符 或 者 字 符 串 的 下 标。
#include<iostream> using namespace std; int main(){ string url ="https://legacy.cplusplus.com/reference/string"; int ret1 = url.find("://");if(ret1 != string::npos){ string s1(url,0, ret1); cout << s1 << endl;} int ret2 = url.find('/', ret1 +3);if(ret1 != string::npos){ string s2(url, ret1 +3, ret2 -(ret1 +3)); cout << s2 << endl;} string s3(url, ret2 +1, url.length()); cout << s3 << endl;return0;}
总 结
通 过 对 STL 及 string 类 的 系 统 梳 理,我 们 不 仅 掌 握 了 容 器、迭 代 器 等 核 心 概 念,更 熟 悉 了 string 类 从 构 造、遍 历 到 增 删 查 改 的 全 流 程 操 作。string 类 凭 借 封 装 性 和 便 捷 接 口,极 大 简 化 了 字 符 串 处 理 工 作。后 续 使 用 中,可 结 合 实 际 场 景 灵 活 调 用 相 关 接 口,同 时 注 意 内 存 管 理 与 效 率 优 化,让 STL 工 具 真 正 成 为 编 程 助 力。