【C++】STL学习之string的使用

在这里插入图片描述
🔥博客主页 小羊失眠啦.
🎥系列专栏《C语言》 《数据结构》 《C++》 《Linux》 《Cpolar》
❤️感谢大家点赞👍收藏⭐评论✍️


在这里插入图片描述

文章目录

  • 前言
  • 一、basic_string
  • 二、编码理解
  • 三、构造函数相关
    • 3.1 无参(默认)构造函数
    • 3.2 带参构造函数
  • 四、容量操作相关
    • 4.1 获取数据
    • 4.2 扩容空间
    • 4.3 调整长度
  • 五、遍历字符相关
    • 5.1 下标访问
    • 5.2 迭代器
  • 六、字符修改相关
    • 6.1 尾插字符/字符串
    • 6.2 任意位置插入字符/字符串
    • 6.3 删除字符/字符串位置
    • 6.4 查找字符/字符串位置
    • 6.5 截取字符串
  • 七、非成员函数
    • 7.1 流操作
    • 7.2 获取字符串
    • 7.3 比较函数

前言

STLC++ 的重要组成部分,由六大部分构成:伪函数空间配置器算法容器迭代器配接器,其中各种各样的 容器 可以很好的辅助我们写程序,比如今天要介绍的 string,有了它之后,我们对字符串的操作就能变得行云流水

在这里插入图片描述

注意: string 诞生于 STL 之前,因此存在部分接口冗余的情况


一、basic_string

stringbasic_string模板 的一份实例,因为字符串多种多样,所以 string 也有各种各样的版本

  • string 常规字符串类,即每个字符占位 1byte

  • wstring 宽字符串类,用来处理较长字符串,Winows下占位 2byte,而 Linux下占位 4byte

  • u16string 匹配 UTF-16 编码标准,指定字符占位 2byteC++11

  • u32string 匹配 UTF-32 编码标准,规定字符占位 4byteC++11

世界上有各种各样的语言,其字符长度大多不一样,因此需要使用不同的 string 来匹配输出自己国家的字符

在这里插入图片描述

在这里插入图片描述


二、编码理解

我们这里介绍的是 string 类,它匹配 UTF-8 标准,而此标准又兼容了 ASCII 码,因此比较常用

ASCII 是美国信息标准交换代码,仅仅通过 1byte 就能满足其字符需求

在这里插入图片描述

UTF-8 的特点是能根据不同范围的字符匹配使用不同的标准,因为ASCII 都是 0xxxxxxx 的形式,当识别到其他字符时,会匹配使用对应标准,比如当识别到汉字时,会使用 GBK 编码标准来进行输出(Windows)

在这里插入图片描述

后续随着万国码 Unicode 的诞生,提出了能适用更多语言的编码标准,即 UTF-16UTF-32 ,而 basic_string 中的 u16stringu32string 这两个类就是用来匹配编码标准的

注: 这两个类是在 C++11 标准中制定的

我们的 string 其实就是 basic_string <char> 的别名

在这里插入图片描述


三、构造函数相关

现在正式进入 string 类的学习,先从默认成员函数—构造函数入手

注意: string 包含于 iostream 头文件中,并且还需要展开 std 命名空间

3.1 无参(默认)构造函数

#include<iostream>
using namespace std;

int main()
{
	string s;   //此时调用的是无参构造函数
	return 0;
}

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

在这里插入图片描述

3.2 带参构造函数

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

int main()
{
	string s("Hello String!");  //指定内容
	//string s = "Hello String!";  //这种写法也是完全可以的
	return 0;
}

在这里插入图片描述

string 也支持将对象构造为 n 个字符 c

int main()
{
	string s(10, 'w'); //构造10个字符w
	return 0;
}

在这里插入图片描述

最后再来看看 string 类的 拷贝构造 函数

int main()
{
	string s1("Hello"); 
	
	string s2(s1);  //将 s1 的内容构造给 s2
	//string s2 = s1;  //这种写法也是可以的

	return 0;
}

在这里插入图片描述


四、容量操作相关

我们可以把 string 类看作一个专门用来处理字符的顺序表,因为它有字符指针容量长度等信息,我们也可以进行手动扩容等操作

4.1 获取数据

获取 string 对象中指向字符串的指针 _str

C++兼容C,在某些场景下需要使用指向字符串的指针,因此 string 类中提供了这个接口

int main()
{
	string s("Hello");
	cout << s.c_str() << endl;
	return 0;
}

在这里插入图片描述

此时直接打印内容的原因是当指针指向对象为常量字符串时,编译器会直接打印内容
我们可以通过强转来观察函数 c_str()

cout << (void*)s.c_str() << endl;  //此时指针非常量字符指针

在这里插入图片描述

通过函数 capapcity()size() 获取当前对象的容量和大小

int main()
{
	string s(200, 'H');  //直接构造200个字符H
	cout << "The string capacity is " << s.capacity() << endl;
	cout << "The string size is " << s.size() << endl;

	//cout << "The string size is " << s.length() << endl;
	return 0;
}

在这里插入图片描述

length() 函数能起到和 size() 函数完全一样的效果, 那为什么会有两个函数呢?

  • string 诞生于 STL 之前,当时的设计的获取大小函数为 length()

  • 后来当 string 并入 STL 后,委员会为了统一化,就在 string 类中添加了一个 size() 函数,因为其他容器中获取大小的函数都是 size()

  • 为了确保向前兼容性,不能直接删除 length(),这里推荐使用 size()

4.2 扩容空间

new 出来的空间不支持像 realloc 一样直接扩容,而是需要通过函数扩容

  • realloc 大多数情况下都是异地扩容,即 开辟-拷贝-销毁-更改指向
  • reserve() 函数实现的就是异地扩容
int main()
{
	string s(10, 'H');  //初始化大小为10
	cout << "The default capacity " << s.capacity() << endl;

	s.reserve(300);     //扩容为300
	cout << "The expansion capacity " << s.capacity() << endl;
	return 0;
}

在这里插入图片描述

VS 中的容量都会稍微多一点

假若我们不手动扩容,string 也会像顺序表一样,识别到容量不够时,自动扩容

VS中 string 的扩容策略

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

Linux中 string 的扩容策略

  • 默认大小为 0 的空间
  • 当第一次扩容时,会先扩至 1
  • 扩容时每次都是 2倍 扩容法,比较清晰
  • 不会多开空间
int main()
{
	string s;
	int capacity = s.capacity();

	cout << "The default capacity " << capacity << endl;
	int n = 0;
	while (n <= 100)
	{
		//尾插字符
		s += 'a';
		if (capacity != s.capacity())
		{
			capacity = s.capacity();
			cout << "The new capacity " << capacity << endl;
		}

		n++;
	}

	return 0;
}

在这里插入图片描述

至于 Windows 中为何如此复杂?首先是 STL 版本不同,其次string 在实际使用中,都用不了太大的空间,因此 VS 就直接索性给了一个默认大小为 15 的数组,后续有需要再进行扩容

频繁扩容会导致内存碎片问题,VS在这里的处理方法是比较合理的

小技巧: 在使用 string 时,可以先提前计算好需要的空间,然后通过 reserve 直接提前扩好,避免因自动扩容而导致的内存碎片问题

4.3 调整长度

除了可以扩容外,我们还可以改变 size

int main()
{
	string s(50, 'w');  //当前的 size 为50
	cout << "The default size " << s.size() << endl;
	cout << "The default capacity " << s.capacity() << endl;

	cout << endl;

	s.resize(30);   //改变 size 为30
	//s.resize(100, 'Z');  //还可以这样写,更改后50块空间为Z
	cout << "The new size " << s.size() << endl;
	cout << "The new capacity " << s.capacity() << endl;

	return 0;
}

在这里插入图片描述

resize() 有两种情况:

  1. 调整后空间比原空间大,此时相当于扩容 reserve(),不过 resize() 还有一个初始化的功能,即将参数2设为指定字符,如果没有指定就默认为 \0
  2. 调整后空间比原空间小,此时将 _size 调整至目标空间,而 _capapcity 不变,此时我们也无法访问到 _size 之外的数据

resize() 并不会缩容,因为缩容的代价比较大,需要先开辟新空间,然后拷贝,释放原空间,才能完成缩容,因此 resize() 在处理时,若新空间比原空间小,是不会改变 _capaciy


五、遍历字符相关

字符串当然少不了遍历操作,主要有三种遍历方式:下标at()迭代器,因为 下标at() 区别不大,所以可以一起介绍,而 迭代器 是一个很重要的东西,后续容器学习中都会出现它的影子

5.1 下标访问

首先来看看 下标访问,实现原理很简单:运算符重载 operator[]

int main()
{
	string s("chatGPT");

	size_t pos = 0;  //下标
	while (pos < s.size())
	{
        //直接像数组一样通过下标访问字符
		cout << "The " << pos + 1 << " char is " << s[pos] << endl;
		pos++;
	}

	return 0;
}

在这里插入图片描述

当我们出现越界行为时,下标访问是直接通过 assert 报错的

下面再来看看 at()

cout << "The " << pos + 1 << " char is " << s.at(pos) << endl;

运行结果与 operator[] 一致,其实这两种方法的实现原理都一样,不过处理问题的方法不一样

当出现越界访问,at() 是抛出异常,而非直接断言报错

总的来说,at() 用的比较少,我们一般都是使用 operator[] 来进行下标的随机访问

5.2 迭代器

下面来看看迭代器 iterator 遍历字符串

int main()
{
	string s("chatGPT");

	//创建迭代器
	string::iterator it = s.begin();  //此时的 it 相当于指向第一个字符的指针
	//auto it = s.begin();   //可以利用 auto 自动识别类型
	while (it != s.end())  
	{
		cout << *it;
		it++;
	}
	cout << endl;

	return 0;
}

在这里插入图片描述

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

除了可以正向遍历外,我们还可以通过反向迭代器 reverse_iterator 进行反向遍历

int main()
{
	string s("chatGPT");

	//创建反向迭代器
	string::reverse_iterator rit = s.rbegin();  //此时的 rit 相当于指向最后一个字符的指针
	//auto it = s.rbegin();   
	while (rit != s.rend())
	{
		cout << *rit;
		rit++;
	}
	cout << endl;

	return 0;
}

在这里插入图片描述

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

迭代遍历区间都是左闭右开

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

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

注意:

  • 迭代器名 const_iterator 中的 const 并非是 const 操作符,而是与普通迭代器构成重载
  • 迭代器不太适合遍历顺序表,适合用来遍历链表
  • 所谓的 范围for 其实就是在调用迭代器进行遍历

六、字符修改相关

现在来谈谈字符修改相关接口

6.1 尾插字符/字符串

尾插字符/字符串有三种方式:

  • push_back() 尾插字符
  • append() 尾插字符/字符串
  • operator+= 尾插字符/字符串

先来看看 push_back()

int main()
{
	string s = "Hello ";

	//尾插字符
	s.push_back('X');

	cout << s << endl;
	return 0;
}

在这里插入图片描述

push_back() 就像是顺序表的尾插,一次只能插入一个字符

再来看看 append()

int main()
{
	string s = "Hello ";

    //尾插字符
	s.append(3, 'X');    //需要指定待插入的字符数
	s.append(" YYYY");   //或者直接插入字符,都是可以的

	cout << s << endl;
	return 0;
}

在这里插入图片描述

append() 还有很多其他用法,感兴趣的可以去查看官方文档

最后再来看看 operator+= ,这个是使用频率最高的,因为比较方便

int main()
{
	string s = "Hello ";

	//尾插字符
	s += 'C';
	s += "SDN";

	cout << s << endl;
	return 0;
}

在这里插入图片描述

在日常使用中,对于字符串尾插这件事,我们通常都是使用 operator+=

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

string 支持在任意位置插入字符/字符串

int main()
{
	string s("cccccc");
	cout << "Begin insert:" << s << endl;

	s.insert(2, 1, 'A');   //在第 n 个位置插入 m 个字符 c
	s.insert(4, "BBBB");   //在第 n 个位置插入字符串

	cout << "After insert:" << s << endl;

	return 0;
}

在这里插入图片描述

insert() 的用法同样还有很多,可以自行查看官方文档

6.3 删除字符/字符串位置

有任意位置插入,当然就有任意位置删除 erase()

int main()
{
	string s("ABCDEFG");
	cout << "Begin erase:" << s << endl;

    //任意位置删除
	s.erase(3, 1);    //从pos开始, 删除第1个字符
	cout << "After erase 1 char:" << s << endl;

	s.erase(2, 4);    //从pos2开始, 删除4个字符
	cout << "After erase 4 char:" << s << endl;

	s.erase();   //默认全删
	cout << "After erase all:" << s << endl;

	return 0;
}

在这里插入图片描述

注意: erase() 是一个全缺省参数,参数1为 0 ,表示默认从 pos0 开始,参数2为 npos,这是无符号整型中的 -1 ,为无符号整型最大值,意思就是如果不写参数2,默认就全删完了

在这里插入图片描述

来看看 npos

在这里插入图片描述

它的值是 4294967295,没有字符串长达 42亿 多,因此可以用来当作默认长度值

6.4 查找字符/字符串位置

它的值是 4294967295,没有字符串长达 42亿 多,因此可以用来当作默认长度值

int main()
{
	string s("My name is Sheep");

    //查找,返回的是目标字符/字符串第一次出现的下标
	cout << "Find 1 char pos: " << s.find('n') << endl; //找字符,默认从pos0开始
	cout << "Find str pos: " << s.find("Sheep", 5) << endl; //找字符串,从pos5开始
	cout << "Find not exist str pos: " << s.find("cow", 10) << endl; //假设没找到

	return 0;
}

在这里插入图片描述

可以看到,当目标不存在时,返回的就是 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() 反向查找

string 类的接口雀氏很多

6.5 截取字符串

int main()
{
	string s("My name is Sheep");

    //利用find 和 substr 切割出 Sheep
	cout << "The target is " << s.substr(s.find('S'), 5) << endl;
	return 0;
}

在这里插入图片描述

其实 substr() 通常用来截取网址中的域名


七、非成员函数

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

7.1 流操作

我们可以直接对 string 对象使用流插入 operator<< 和流提取 operator>>

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

在这里插入图片描述

7.2 获取字符串

单纯的流插入是无法满足字符串插入需要的,因为字符串中往往都会包含 ' ',而 cin 会认为这是结束标志,进而不再读取字符,因此有专门的函数获取字符串 getline()

#include<string>

int main()
{
	string s;
	getline(cin, s);	//需要包含头文件 string
	cout << s;
	return 0;
}

在这里插入图片描述

注意: 需要包含头文件 string

7.3 比较函数

string 类中存在一系列的大小比较函数(18个),光是判断相等就有3个,其实没必要设计这么多函数,这可能也是 string 饱受别人吐槽的原因之一

在这里插入图片描述

在这里插入图片描述

原文出处:《STL中的string类怎么啦?》

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣爆刷第114天之CodeTop100五连刷56-60

力扣爆刷第114天之CodeTop100五连刷56-60 文章目录 力扣爆刷第114天之CodeTop100五连刷56-60一、78. 子集二、105. 从前序与中序遍历序列构造二叉树三、43. 字符串相乘四、155. 最小栈五、151. 反转字符串中的单词 一、78. 子集 题目链接&#xff1a;https://leetcode.cn/prob…

一文了解RAID技术基本概念

RAID是数据存储技术&#xff0c;旨在提高磁盘的IO吞吐以及提供更为可靠的数据安全。在实际工作中经常听到RAID相关名称&#xff0c;那么RAID技术的基本概念是什么、不同RAID级别有什么特性&#xff0c;本文将简单介绍&#xff0c;以了解。 1、RAID技术基本概念 1.1 RAID基本概…

uniapp中uni.navigateTo传递变量

效果展示&#xff1a; 核心代码&#xff1a; uniapp中uni.navigateTo传递变量 methods: {changePages(item) {setDatas("maintenanceFunName", JSON.stringify(item)).then((res) > {uni.navigateTo({url: /pages/PMS/maintenance/maintenanceTypes/maintenanceT…

python开发poc2,爆破脚本

#本课知识点和目的&#xff1a; ---协议模块使用&#xff0c;Request 爬虫技术&#xff0c;简易多线程技术&#xff0c;编码技术&#xff0c;Bypass 后门技术 下载ftp服务器模拟器 https://lcba.lanzouy.com/iAMePxl378h 随便创建一个账户&#xff0c;然后登录进去把ip改成…

从头开发一个RISC-V的操作系统(四)嵌入式开发介绍

文章目录 前提嵌入式开发交叉编译GDB调试&#xff0c;QEMU&#xff0c;MAKEFILE练习 目标&#xff1a;通过这一个系列课程的学习&#xff0c;开发出一个简易的在RISC-V指令集架构上运行的操作系统。 前提 这个系列的大部分文章和知识来自于&#xff1a;[完结] 循序渐进&#x…

五一假期来临,各地景区云旅游、慢直播方案设计与平台搭建

一、行业背景 经文化和旅游部数据中心测算&#xff0c;今年清明节假期3天全国国内旅游出游1.19亿人次&#xff0c;按可比口径较2019年同期增长11.5%&#xff1b;国内游客出游花费539.5亿元&#xff0c;较2019年同期增长12.7%。踏青赏花和户外徒步成为假期的热门出游主题。随着…

Matlab 修改图例顺序

对于使用 .m 文件绘制的图片&#xff0c;可以修改程序中图例的顺序来改变图片的图例。如果图片所对应的 .fig 文件已经存在&#xff0c;而且不便修改源程序&#xff0c;则可以通过如下方式来修改图例&#xff1a; step 1: 打开fig文件&#xff0c;然后点击绘图浏览器 step 2&…

STC89C51学习笔记(五)

STC89C51学习笔记&#xff08;五&#xff09; 综述&#xff1a;文本讲述了代码中速写模板的创建、如何将矩阵键盘的按键与数字一一对应以及如何创建一个矩阵键盘密码锁。 一、速写模板 点击“templates”&#xff0c;再鼠标右键选择配置&#xff0c;按照以下方式即可修改一些…

【WEEK6】 【DAY7】MD5 Encryption Transactions【English Version】

2024.4.7 Sunday Following the previous article 【WEEK6】 【DAY3】MySQL Functions【English Version】 Contents 5.3. MD5 Encryption5.3.1. Introduction5.3.2. Testing MD5 Encryption5.3.2.1. Plain Text Passwords5.3.2.2. Implementing Data Encryption5.3.2.3. Encry…

位域与联合体巧妙使用

在编写dbc报文的协议解析时&#xff0c;使用位域运算和联合体的组合&#xff0c;能够巧妙解决字段解析问题&#xff0c;代码看起来整洁又健壮。 #include <algorithm> #include <iostream> #include <vector>using namespace std;typedef union tagCoreCalib…

Python爬虫:为什么你爬取不到网页数据

目录 前言 一、网络请求被拒绝 二、数据是通过JavaScript加载的 三、需要进行登录 四、网站反爬虫策略 五、网站结构变更 总结 前言 作为一名开发者&#xff0c;使用Python编写爬虫程序是一项常见的任务。爬虫程序的目的是收集互联网上的数据&#xff0c;并将其保存或使…

【漏洞复现】用友畅捷通TPlus GetStoreWarehouseByStore .net反序列化漏洞

0x01 阅读须知 “如棠安全的技术文章仅供参考&#xff0c;此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供…

【Lavavel框架】——各目录作用的介绍

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

C++ 之 【类与对象】从入门到精通一条龙服务 入门篇

不要觉的自己很没用&#xff0c;其实你还可以给家人带来温暖&#xff0c;比如爸妈看到你就来火 目录&#xff1a; 一、面向过程和面向对象初步认识 二、类的引入 三、类的定义 四、类的访问限定符及封装 1.访问限定符 2.封装 五、类的作用域 六、类的实例化 七、类的…

亚马逊云配置深度学习环境

开发环境 对于MacOS来说没无法cuda&#xff0c;所以用的是mps后端计算&#xff0c;所以也无法打印GPU数量和型号。 torch.backends.mps.is_available()如果使用PopOS的话&#xff0c;可以选择自带英伟达驱动的镜像&#xff0c;这样就可以免去复杂的驱动安装流程。 EC2 (自己…

RabbitMQ的交换机与队列

一、流程 首先先介绍一个简单的一个消息推送到接收的流程&#xff0c;提供一个简单的图 黄色的圈圈就是我们的消息推送服务&#xff0c;将消息推送到 中间方框里面也就是 rabbitMq的服务器&#xff0c;然后经过服务器里面的交换机、队列等各种关系&#xff08;后面会详细讲&am…

计算机毕业设计PHP+vue+mysql外卖订餐配送系统

本系统的设计与实现共包含17个表:分别是关于我们信息表&#xff0c;菜品分类信息表&#xff0c;菜品信息信息表&#xff0c;配置文件信息表&#xff0c;订单信息信息表&#xff0c;菜品信息评论表信息 表&#xff0c;留言反馈信息表&#xff0c;美食资讯信息表&#xff0c;配送…

白色磨砂质感html5页源码

白色磨砂质感html5页源码&#xff0c;简约的基础上加上了团队成员&#xff0c;自动打字特效音乐播放器存活时间 源码下载 https://www.qqmu.com/2980.html

牛客NC93 设计LRU缓存结构【hard 链表,Map Java】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/5dfded165916435d9defb053c63f1e84 思路 双向链表map最新的数据放头结点&#xff0c;尾节点放最老的数据&#xff0c;没次移除尾巴节点本地考察链表的新增&#xff0c;删除&#xff0c;移动节点参考答案Java im…

K8s学习四(资源调度_1)

资源调度 发现对Pod操作不方便&#xff0c;不能直接操作&#xff0c;而且不能直接编辑&#xff0c;需要对原来的配置文件进行操作&#xff0c;而且需要删除之后再创建Pod&#xff0c;不方便&#xff0c;更多是通过控制器来操作。 Label和Selector 通过设置标签和选择器来确定…