C++【STL】之string的使用

news2024/11/24 19:16:27

STL简介

STLC++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。STL由六大组件构成:仿函数算法迭代器空间配置器容器配接器

其中各种容器可以很大帮助的提升我们编写程序的效率,后续都会一一介绍,今天我们就先拿!string来小试牛刀,用上了它,我们就能对字符串的操作更加行云流水。

string使用

注意:string诞生于STL之前,所以会存在接口冗余的现象,本文介绍的是string的部分常用接口,大佬们想了解更多关于string类的细节,请前往官方文档(点击跳转)查阅

1. 构造函数

注意:string包含于iostream头文件中,使用时还需展开std命名空间

1.1 默认(无参)构造函数

int main()
{
	string s; //调用默认构造函数,只包含'\0'
	return 0;
}

调用默认构造函数时,默认将对象初始化为只包含 '\0' 的空串

1.2 带参构造函数

我们可以自己指定string对象中的内容

int main()
{
	string s1("happy"); //带参构造函数,指定字符串+'\0',下同
	string s2 = "happy";
	string s3(12, 'c'); //构造12个c
	return 0;
}

1.3 拷贝构造

int main()
{
	string s1("happy");
	string s2 = "happy";

	string s3(s1); //拷贝构造,下同
	string s4 = s1;
	return 0;
}

2. 容量操作

string类中包含容量、长度、字符指针等,我们可以将其看作一个存放字符的顺序表来处理

2.1 获取数据

c_str() 接口:获取string对象中指向字符串的指针,当指针指向对象为常量字符串时,编译器会直接打印内容

int main()
{
	string s1("hello");
	string s2 = "happy";
	//指针指向常量字符串时编译器直接打印
	cout << s1.c_str() << endl; //获取对象s1中的字符串指针
	cout << (void*)s2.c_str() << endl; //非常量字符指针
	return 0;
}

capacity()接口和size()接口:获取string对象的容量和大小

int main()
{
	string s(88, 'j');
	cout << "capacity: " << s.capacity() << endl;
	cout << "size: " << s.size() << endl;
	return 0;
}

2.2 空间扩容

new出来的空间不支持使用relloc直接扩容,而需要使用专门的接口来扩容

reserve()接口:实现string对象异地扩容

int main()
{
	string s(12, 's'); //初始容量为12
	cout << "capacity: " << s.capacity() << endl;

	s.reserve(24); //扩容为24
	cout << "capacity: " << s.capacity() << endl;
	return 0;
}

如果我们不手动扩容,string 也会在识别到容量不够时,自动扩容

VS下string 的扩容规则

  • 默认给一个大小为15的数组存储数据,当数组够用时,都是用的数组
  • 当数组容量不够时,改用指针,先2倍扩容至30,后续字符都是存在指针中,之后每次扩容,都是1.5倍扩容
  • 会多开辟一些空间

Linux下string 的扩容规则

  • 默认大小为0的空间
  • 当第一次扩容时,会先扩至1,之后每次都是2倍扩容
  • 不会多开空间

注意:频繁的扩容会导致内存碎片问题,我们在使用string时,可以先提前计算好大致需要的空间,使用reserve提前进行扩容,来避免此问题

2.3 长度调整

size()接口:改变string对象的长度

int main()
{
	string s(24, 'a'); //初始size = 24
	cout << "size: " << s.size() << endl;

	s.resize(12); //改变size为12
	//s.resize(36, 'b'); //改变后12块空间为b
	cout << "size: " << s.size() << endl;
	return 0;
}

resize()接口的两种情况:

  • 如果调整后空间比原空间大,就相当于扩容,resize() 还有初始化的功能,可以将第二个参数设置为指定字符,如果没有指定就默认为 \0,如以上示例。
  • 如果调整后空间比原空间小,此时将会size调整至目标空间,而capapcity不会改变,此时我们无法访问到size之外的数据

resize() 并不会达到缩容的效果,因为缩容的代价比较大,需要先开辟新空间,然后拷贝,释放原空间,因此 resize() 在处理时,若新空间比原空间小,不会改变capaciy

3. 字符串遍历

string字符串的遍历主要有三种方式:下标访问[]at()迭代器访问

3.1 下标访问

下标访问的原理就是运算符重载operator[]

int main()
{
	string s("hello sakura!");
	size_t pos = 0;
	while (pos < s.size())
	{
		cout << s[pos++]; //hellosakura
	}
	return 0;
}

at()访问字符串

int main()
{
	string s("hello sakura!");
	size_t pos = 0;
	while (pos < s.size())
	{
		cout << s.at(pos++);
	}
	return 0;
}

这里的运行结果和上面使用下标访问时一致的,二者实现的原理一样,区别是当出现越界访问时,at()抛异常,而下标访问会通过assert报错

3.2 迭代器

迭代器遍历iterator字符串

使用begin()获取第一个字符,end()获取最后一个字符的下一个字符即'\0'

int main()
{
	string s("hello world!");
	string::iterator it = s.begin(); //此时it相当于指向第一个字符的指针
	//auto it = s.begin(); //也可以用auto自动类型识别
	while (it != s.end())
	{
		cout << *it;
		it++;
	}
	return 0;
}

除了可以使用iterator进行正向遍历外,还可以使用reverse_iterator进行反向遍历

rbegin() 获取最后一个字符,rend() 获取第一个字符的前一个字符

int main()
{
	string s("hello world!");
	string::reverse_iterator rit = s.rbegin(); //此时rit相当于指向最后一个字符的指针
	while (rit != s.rend())
	{
		cout << *rit;
		rit++;
	}
	return 0;
}

除了上面两种普通迭代器外,还有两个 const 修饰的迭代器,用来遍历常量字符串

  • const_iterator正向遍历常量字符串
  • const_reverse_iterator反向遍历常量字符串

注意:

  • 迭代器遍历的区间都是左闭右开的
  • 迭代器名中 的 const 并不是 const 操作符,而是与普通迭代器构成重载
  • 迭代器不适合用来遍历顺序表,适合遍历链表
  • 范围for的底层就是调用迭代器遍历

4. 字符串修改

4.1 尾插字符/字符串

对于尾插字符/字符串,string提供了三种方式:

首先是push_back():尾插字符


int main()
{
	string s = "happ";
    cout << s << endl;
	//尾插字符
	s.push_back('y');
	cout << s << endl;
	return 0;
}

push_back()类似顺序表尾插,一次只能插入一个字符

第二种是append():尾插字符字符串

int main()
{
	string s = "happy";
	cout << s << endl;
	//尾插字符/字符串
	s.append(2, 'aa'); //插入两个a
	s.append(" cjc"); //插入字符串
	cout << s << endl;
	return 0;
}

第三种是operator+=:尾插字符/字符串

int main()
{
	string s = "happy";
	cout << s << endl;	
	//尾插字符/字符串
	s += 'c';
	s += "jc";
	cout << s << endl;

	return 0;
}

在日常使用中operator+=是使用频率最高,最方便的

4.2 任意位置插入字符/字符串

insert()接口:支持在string对象任意位置插入字符/字符串

int main()
{
	string s("xxxxxx");
	cout << s << endl; //xxxxxx
	s.insert(2, 1, 'c'); //在第2个位置插入1个c
	s.insert(3, "jc"); //在第3个位置插入字符串jc
	cout << s << endl; //xxxcjcxxx
	return 0;
}

4.3 任意位置删除字符/字符串

erase()接口:支持在string对象任意位置删除字符/字符串

int main()
{
	string s("happynewyear");
	cout << s << endl;
	s.erase(5, 1); //从第5个位置开始删除1个字符
	cout << s << endl;
	s.erase(5, 2); //从第5个位置开始删除2个字符
	cout << s << endl;
	s.erase(); //默认全部删除
	cout << s << endl; //空串
	return 0;
}

image-20230612004053893

erase() 是一个全缺省参数,第一个参数为0,表示默认从pos[0]开始,第二个参数为npos,这是无符号整型中的-1,为无符号整型最大值,意思是如果不写第二个参数,默认就全部删除

image-20230612004950247

4.4 查找字符/字符串位置

find()接口:查找string对象中指定字符/字符串的位置

int main()
{
	//返回的是目标字符/字符串第一次出现的下标
	string s("happycjchappy");
	//找字符c,默认从pos0位置开始找
	cout << s.find('c') << endl;
	//找字符串ha
	cout << s.find("ha") << endl;
	//找字符串ha,从pos3位置开始找
	cout << s.find("ha", 3) << endl;
	//没找到的情况
	cout << s.find("bb") << endl; //返回npos
	return 0;
}

find返回的是目标字符/字符串第一次出现的下标,这里可以看到,当目标不存在时,返回的就是 npos

find()还有另外几种使用方式:

  • rfind()从后往前找
  • find_first_of(str, pos = 0)pos位置往后,找str中出现的任意字符
  • find_last_of(str, pos = npos)npos位置往前,找str中出现的任意字符
  • find_first_not_of()反向查找
  • find_last_not_of()反向查找

4.5 截取字符串

substr()接口:截取string对象中的目标字符串

int main()
{
	string s("happy new year");
	cout << s.substr(s.find('n'), 3) << endl;
	return 0;
}

5. 非成员函数

string中还有很多定义在类外的非成员函数

5.1 流操作

可以直接对string对象进行流插入operator<<和流提取operator>>

int main()
{
	string s;
	cin >> s;
	cout << s;
	return 0;
}

5.2 获取字符串

当输入的字符串中包含 ' '(空格)时,cin 会认为这是结束标志,而不再继续读取字符,因此单纯的流插入是无法满足字符串插入需要的,为此string专门的函数获取字符串 的接口getline()

int main()
{
	string s;
	getline(cin, s);
	cout << s;
	return 0;
}

5.3 比较函数

string类中的比较函数特别冗余,饱受吐槽,友友们需要时可以到官方文档查阅


C++【STL】之string的使用,到这里就介绍结束了,本篇文章对你由帮助的话,期待大佬们的三连,你们的支持是我最大的动力!

文章有写的不足或是错误的地方,欢迎评论或私信指出,我会在第一时间改正!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/635659.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

静态NAT配置与验证实验

静态NAT配置与验证实验 【实验目的】 部署静态NAT。熟悉静态NAT的应用方法。验证配置。 【实验拓扑】 实验拓扑如图所示。 实验拓扑 设备参数如表所示。 设备参数表 设备 接口 IP地址 子网掩码 默认网关 R1 f0/0 192.168.10.1 255.255.255.0 N/A S1/0 10.0.0.1…

GlyphControl: Glyph Conditional Control for Visual Text Generation

GlyphControl: Glyph Conditional Control for Visual Text Generation (Paper reading) Yukang Yang, Microsoft Research Asia, arXiv2023, Cited: 0, Code, Paper 1. 前言 最近&#xff0c;人们对开发基于扩散的文本到图像生成模型的兴趣日益增长&#xff0c;这些模型能够…

软件工程开发文档写作教程(11)—需求分析书的编写

本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl本文参考资料&#xff1a;电子工业出版社《软件文档写作教程》 马平&#xff0c;黄冬梅编著 需求分析书主要内容 按照国家《软件需求说明书GB8567-88》所定义的标准&#xff0c;软件需求…

2023去水印小程序saas系统源码修复独立版v1.0.3+uniapp前端

&#x1f388; 限时活动领体验会员&#xff1a;可下载程序网创项目短视频素材 &#x1f388; &#x1f389; 有需要的朋友记得关赞评&#xff0c;阅读文章底部来交流&#xff01;&#xff01;&#xff01; &#x1f389; ✨ 源码介绍 一个基于uniapp写的小程序&#xff0c;后端…

MATLAB | 绘图复刻(九) | 泰勒图及组合泰勒图

有粉丝问我这个图咋画&#xff1a; 我一看&#xff0c;这不就泰勒图嘛&#xff0c;就fileexchange上搜了一下泰勒图绘制代码&#xff0c;但是有的代码比较新的版本运行要改很多地方&#xff0c;有的代码需要包含一些压缩包没并没有的别人写的函数&#xff0c;于是我干脆自己写了…

JAVA-八种基础数据类型和包装类型及相关面试题

文章目录 前言一、基本数据类型1.1 分类1.2 概念1.3 代码1.4 二维表 二、各基本数据类型间强制转换2.1 为什么Java中有强制转换&#xff1f;2.2 示例代码 三、包装类型3.1 为什么有包装类型&#xff1f;3.2 基本概念3.3 转换方法 四、转换过程中使用的自动装箱和自动拆箱4.1 来…

Redis Lua脚本原理

Lua脚本执行过程 创建并修改Lua环境 1 创建基础Lua环境2 载入函数库3 创建全局表格Lua4 替换随机函数5 创建排序辅助函数6 创建redis.pcall函数7 全局环境保护8 修改后的Lua环境保存到服务器状态的Lua属性&#xff0c;等待脚本执行 Redis中带有不确定性的命令&#xff1a; …

RK3588平台开发系列讲解(以太网篇)PHY状态机

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、PHY状态机定义二、PHY的状态变化三、PHY的状态变化打印沉淀、分享、成长,让自己和他人都能有所收获!😄 一、PHY状态机定义 phy状态机: 目录:include/linux/phy.h enum phy_state {PHY_DOWN = 0,</

开源模型的力量

2 月&#xff0c;Meta 发布了其大型语言模型&#xff1a;LLaMA。与 OpenAI 及其 ChatGPT 不同&#xff0c;Meta 不仅仅为世界提供了一个可以玩的聊天窗口。 相反&#xff0c;它将代码发布到开源社区&#xff0c;此后不久模型本身就被泄露了。研究人员和程序员立即开始修改、改…

Protobuf实战:通讯录

网络版通讯录 需求 Protobuf常⽤于通讯协议、服务端数据交换场景。接下来将实现⼀个⽹络版本的通讯录&#xff0c;模拟实现客⼾端与服务端的交互&#xff0c;通过Protobuf来实现各端之间的协议序列化。 需求如下&#xff1a; 客⼾端可以选择对通讯录进⾏以下操作&#xff1a;…

电子科技大学编译原理复习笔记(七):自下而上语法分析

目录 前言 重点一览 引言 自下而上分析 分析方法 规范规约&#xff08;最左规约&#xff0c;对应最右推导&#xff09; 算符优先分析法 算符优先文法 最左素短语 举个例子 优先关系表的构造 规范规约与算符优先分析 LR分析法 概述 LR&#xff08;0&#xff09…

系统架构设计师 2:计算机基础

一、计算机硬件 1 处理器&#xff08;CPU&#xff09; 处理器是计算机系统运算和控制的核心部件。 1.1 指令集 处理器的指令集按照其复杂程度可分为复杂指令集&#xff08;CISC&#xff09;与精简指令集&#xff08;RISC&#xff09;。 随着研究的深入&#xff0c;RISC已经…

基于深度学习的高精度安全背心检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度安全背心检测识别系统可用于日常生活中或野外来检测与定位安全背心目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的安全背心目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5…

2.25 sigprocmask函数使用 2.26sigaction信号捕捉函数 2.27SIGCHILD信号

2.25 sigprocmask函数使用 阻塞信号集有时称作信号掩码。 联想&#xff1a;fcntl函数可以修改fd属性。 ./sigprocmask & //将程序设置为后台运行&#xff0c;输入ls可以同步有输出 fg //将程序恢复到前台运行#include <stdio.h> #include <signal.…

动态规划dp —— 21.乘积最大子数组

1.状态表示 是什么&#xff1f;dp表中里的值所表示的含义就是状态表示 因为要考虑负数情况&#xff0c;负数乘以最大数就等于最小数了&#xff0c;负数乘以最小数就是最大数了 f[i]表示&#xff1a;以i位置为结尾的所以子数组中最大乘积 g[i]表会&#xff1a;以i位置为结尾…

Java 实现删除顺序表中第一次出现的某个元素

一、思路 1.顺序表不能是空的&#xff0c;如果顺序表是空的就肯定无法删除第一次出现的 key 元素. 2.定义一个key变量来传入要删除的元素&#xff0c;这个元素要求是第一次出现的. 3.删除之前要先找到第一次出现的key的下标. 4.找到位置之后就开始删除. 5.删除过程是从key下标位…

Linux - struct file与缓冲区

​​​​​​​ ​​​​​​​ 感谢各位 点赞 收藏 评论 三连支持 本文章收录于专栏【Linux系统编程】 ❀希望能对大家有所帮助❀ 本文章由 风君子吖 原创 ​​​​​​​ ​​​​​​​ ​​​​​​​ 前言 对于文件&#x…

DTS迁移Oracle至DM与MySQL至DM

目录 DTS迁移Oracle至DM... 3 一、前期准备... 3 二、DTS迁移... 4 1、新建工程... 5 2、新建迁移... 6 3、填写源库信息&#xff0c;使用指定驱动并自定义URL连接... 7 4、填写目标库信息... 8 5、填写迁移选项... 8 6、指定迁移模式及模式对象... 9 7、执行迁移...…

http长连接与会话保持

"我们半推半就的人生&#xff0c;没有和你一样被眷顾的未来!" 一、Http长连接 (1) 为什么需要长连接 如上展示的是一个常规得并不能再常规的http服务&#xff0c;从本地拉取远端linux上的本地文件上传至浏览器上&#xff0c;经过浏览器的渲染展示成如今的样子。唔&a…

【实战】体验训练Geneface

一.环境 conda activate geneface export PYTHONPATH./ CUDA_VISIBLE_DEVICES0 python tasks/run.py --configegs/datasets/lrs3/lm3d_syncnet.yaml --exp_namelrs3/syncnet 训练这篇出过的一些奇奇怪怪的问题基本上都记录在【环境搭建】40系一些奇奇怪怪的环境问题_weixin_50…