C++:String类的使用

     

                                           创作不易,感谢三连!! 

      在C语言中,我们想要存储字符串的话必须要用字符数组

char str[]="hello world"

       这其实是将在常量区的常量字符串拷贝到数组中,我们会在数组的结尾多开一个空间存储\0,这样我们如果想在访问的时候,比如打印,我们总是认为这个字符串是会读取到\0结束的

但是过于依赖\0也会有一系列的问题:

1、如果我是一个很长的字符串,但是中间有几个/0,那么我很难直接打印出来全部的字符串,因为访问到\0就会卡住

2、如果我们想通过键盘输入hello world,我们把它当成一个字符串,但是cin和scanf会默认访问到第一个空格或者是换行符就结束。

也就是说,我们的字符串如果有空格,那么还得分批次打印……

3、如果我想减少、增加、修改……这个字符串中的某些字符,也十分麻烦……如果是静态字符数组,会面临空间不够的问题,如果是动态字符数组,那么我们还要时刻注意空间的管理,稍不留神就会越界访问。

 4、虽然C语言中提供了一系列的str类的库函数,但是这些库函数都是以字符串分离开的,没有把该字符串作为一个整体,并且也容易受到\0的影响。这并不符合C++面向对象的思想。

      基于此,我们的祖师爷在想,能不能把string封装成一个类,把它像顺序表一样管理起来,给他增设一些常用的比如增删查改的函数接口?针对扩容进行检查?利用构造函数和析构函数帮助我们管理内存呢??因而我们的string类就诞生了!!

一、标准库中的string类

想要学习strling类,就要去通过他的文档去了解

string类的文档介绍

       诶!!我们发现string类竟然是一个叫做basic_strling的类模版生成的?难道string类还能有其他版本??没错!!string类有很多版本

 为什么string要有这么多版本呢??原因是这样的:

C语言最早都是由老美发明的,他们研究出了ascii码表

       为什么要发明这个东西呢??因为我们的语言和计算机的语言是不一样的,计算机只能看得懂二进制的语言,所以我们为了能让计算机看懂我们的语言,我们必须要给一个映射表, 比如我们想打印apple,那么计算机通过这个ASCII码表来匹配,比如a对应的是97 p对应的是112 l对应的是108 e对应的是101.我们通过调试转出10进制是可以验证这个结果的。

      一个字节是8个比特位,所以最多其实可以有256个表示的方法。而美国的英文字母才26个,哪怕算上大写,算上数字,算上一些符号,128个,也就是7bit就足够了!!所以ASCII码在使用英文的国家是非常友好的,每个字节都可以存储一个字符,这样就都可以表示出来。

      但是老美也想把技术推广到其他国家啊!!比如中国汉字特别多,如果全部像ASCII码表一样都按照一个字节的方式,那汉字远远不止256个啊!

      随着计算机的发展,不同国家也出现了很多字符编码,但是由于字符编码不同,计算机在不同国家之间的交流变得很困难,经常会出现乱码的问题,比如:对于同一个二进制数据,不同的编码会解析出不同的字符

     当互联网迅猛发展,地域限制打破之后,人们迫切的希望有一种统一的规则, 对所有国家和地区的字符进行编码,于是 Unicode 就出现了 

 unicode又有三个版本:

1、UTF-8

 UTF-8: 是一种变长字符编码,被定义为将码点编码为 1 至 4 个字节,具体取决于码点数值中有效二进制位的数量

UTF-8 的编码规则:

  1. 对于单字节的符号,字节的第一位设为 0,后面 7 位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的, 所以 UTF-8 能兼容 ASCII 编码,这也是互联网普遍采用 UTF-8 的原因之一
  2. 对于 n 字节的符号( n > 1),第一个字节的前 n 位都设为 1,第 n + 1 位设为 0,后面字节的前两位一律设为 10 。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码

 2、UTF-16

UTF-16 也是一种变长字符编码, 这种编码方式比较特殊, 它将字符编码成 2 字节 或者 4 字节

具体的编码规则如下:

  1. 对于 Unicode 码小于 0x10000 的字符, 使用 2 个字节存储,并且是直接存储 Unicode 码,不用进行编码转换
  2. 对于 Unicode 码在 0x10000 和 0x10FFFF 之间的字符,使用 4 个字节存储,这 4 个字节分成前后两部分,每个部分各两个字节,其中,前面两个字节的前 6 位二进制固定为 110110,后面两个字节的前 6 位二进制固定为 110111, 前后部分各剩余 10 位二进制表示符号的 Unicode 码 减去 0x10000 的结果
  3. 大于 0x10FFFF 的 Unicode 码无法用 UTF-16 编码

 3、UTF-32

      UTF-32 是固定长度的编码,始终占用 4 个字节,足以容纳所有的 Unicode 字符,所以直接存储 Unicode 码即可,不需要任何编码转换。虽然浪费了空间,但提高了效率。

       UTF-8、UTF-16、UTF-32 是 Unicode 码表示成不同的二进制格式的编码规则,同样,通过这三种编码的二进制表示,也能获得对应的 Unicode 码,有了字符的 Unicode 码,按照上面介绍的 UTF-8、UTF-16、UTF-32 的编码方法 就能转换成任一种编码了

    总结:由于UTF-8兼容ASCII,这是他的最大优势,并且在我们日常编程中很多情况只需要一个字节就可以,虽然字节存储的时候需要用一些比特位来区分该字符是几个字节的,但是从效率上来说,他可以根据具体的情况去决定用几个字节去存储,所以互联网大多采用UTF-8,而UTF-16也是变长,他的起点就是两个字节,两个字节其实可以表示完大部分国家的字符了,只有极少数古老的文字可能会需要用到4个字节。UTF-32就很粗暴,无论什么都是用4个字节,所以足够容纳所有的Unicode字符,虽然浪费了空间,但是不需要任何的编码转换,效率会比较高。但是使用得很少,在C11的时候引入了u32string。

 简单介绍GBK:

    但是微软使用的主要还是GBK,Windows支持GBK的时候UTF-8还没有普及,而微软是一家及其看重存量客户和兼容性的公司,形成了路径依赖不能轻易改变。

     GBK是大部分用两个字节表示,一些比较生僻的用三个字节表示

并且一般都是把读音类似的编在一起

      以上只是浅浅了解为什么string有这么多个版本,下面开始研究string类!!

二、string类的接口说明

2.1 string对象常见构造(constructor)

 我们研究几个比较重要的

1、string()

构造空的string对象,即空字符串

2、string(const char* s)

用C-string来构造string类对象

 其实相当于将常量字符串拷贝到str中

3、string(const string&s) 

拷贝构造函数

 

拷贝构造其实是深拷贝,因为str1和str2指向的是不同的空间

4、string(size_t n, char c)

意思是用几个相同的字符去构造

5、string (const string& str, size_t pos, size_t len = npos);

意思是,给一个string类,读取从他的第pos个位置开始往后的len个字符。

如果len给大了,就全部打印完

 我们还注意到len如果不给,会给一个缺省值nops,nops是什么呢?

 我们可以看到npos是string类里面的一个静态成员变量,他的值是-1,但是由于是size_t类型,所以转化成无符号整型是一个特别大的数

所以如果我们不传的话,也是直接拷贝完。跟传太大是一样的

 6、string (const char* s, size_t n);

 

 给一个常量字符串,从第一个位置拷贝到第n个位置

总结: 5和6易混淆,第一个传str类,然后从第pos个位置开始拷贝len个字符,第二个是传常量字符串,从第一个位置拷贝到n位置。

 2.2 string类对象的容量操作(Capacity)

 1、size和length

size和length其实是一样的, 都代表字符串的长度,但是早期STL还没出现的时候,strling类用的是length,但是后来STL出来后,里面大部分都是用的size,所以为了保持一致性又造了一个size出来,平时用哪个都可以的。

 2、capacity

表示string当前的容量,一般来说是默认不算上/0

 这边其实是16,但是capacity不会吧/0算进去。相当于capacity是实际容量-1

那string类究竟是如何扩容的呢?我们来用一段代码观察一下:

 不同编译器存在差异,因为用的STL版本可能不一样,我们看看g++

对比后我们可以看到区别:

    vs用的是PJ版STL,字符串会先被存在_Bx数组中,超过之后才会去动态开辟空间,第一次扩容是从32开始进行1.5倍扩容。而g++是SGI版STL,直接就是从0开始,然后根据情况直接开始扩容,从0开始进行2倍扩容

3、reverse

作用是提前去增加容量,扩容的效率是很低的,所以如果我们知道这个string类最多需要空间,我们一次性开好,可以减少拷贝。如果是减少的话,不会缩容。

他根据1.5倍的扩容规则,至少会扩容到超过你的要求。如果是g++的话,就是刚好到你要求的

 4、resize

会去改变size,减少的话就是变少(不会改变容量),如果增多的话就可能会扩容顺便帮助我们初始化,第一个版本的话初始化补\0,第二个版本的话就是补自己想要初始化的内容

 5、empty

 字符串为空返回1,不为空返回0

6、clear

 清楚字符串,变成空字符串(不会缩容)

7、shrink_to_fit

 意思是缩容到刚好够容纳他的有效字符个数

如上图一开始的空间是32,然后我们将size变成5,然后进行缩容到刚好够容纳他的大小

 所以前面的resize和reverse如果减少的话,都是不会改变容量大小的!!clear也不会

7、小总结

1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一
致,一般情况下基本都是用size()。
2. clear()只是将string中有效字符清空,不改变底层空间大小。
3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用\0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于
string的底层空间总大小时,reserver不会改变容量大小。

5.缩容的shrink_to_fit尽量不要用,意义不大,而且任何一个接口都不会帮我们缩容

2.3 string类对象的访问及遍历操作(Iterators

1、operator[ ] / at
 

访问下标为pos的字符,at和他是一样的功能 

 2、begin+end

获取一个字符的迭代器+获取最后一个字符的迭代器,要用到iterator(迭代器)

可以把迭代器理解成指针,begin是开始的指针,end是指向\0的指针,注意是左闭右开!! 

如果是用const类型的迭代器,就不能修改 

至此我们知道了三种可以访问的字符串的方法: 1、下标遍历 2、迭代器遍历 3、范围for

3、rbegin+rend

跟上面差不多,只不过是从尾开始访问

有没有感觉string::reverse_iterator有点难写,那用auto是不是更香

 2.4 string类对象的修改操作(Modifiers)

1、push back

尾插一个字符

2、 string::operator+= 

 3.append

(1) string& append (const string& str);

直接尾插一个str对象

(2)string& append (const string& str, size_t subpos, size_t sublen);

从这个字符串的subpos位置往后的sublen个字符尾插

(3)string& append (const char* s)

尾插一个字符串

(4)string& append (const char* s, size_t n);

尾插字符串中的前n个字符

 (5)string& append (size_t n, char c);

尾插n个字符

 一般来说我们更喜欢用+=,除非是一些特殊情况才会去用append

4,insert 

(1) string& insert (size_t pos, const string& str)和string& insert (size_t pos, const char* s);

从第pos个位置开始插入字符

(2)string& insert (size_t pos, const string& str, size_t subpos, size_t sublen)

是从被插入字符串中的subpos位置往后选len个字符插入

(3) string& insert (size_t pos, const char* s, size_t n);

是从被插入字符串中选前n个字符插入

(4)string& insert (size_t pos, size_t n, char c);

从第pos个位置开始插入n个相同字符

5.erase 

 从pos位置开始往后删除len个字符,不穿nops默认就pos后面全删

一般来说insert和erase都可能设计到大量数据的移动,所以不建议使用!! 

6,pop_back

尾删一个字符

7,replace

(1)string& replace (size_t pos, size_t len, const string& str)和string& replace (size_t pos, size_t len, const char* s);

将pos位置开始后面的len个字符替换成别的字符串

 (2)string& replace (size_t pos, size_t len, const string& str, size_t subpos, size_t sublen);

是从被替换字符串中的subpos位置往后选sublen个字符插入

(3)string& replace (size_t pos, size_t len, const char* s, size_t n);

是从被替换字符串中选前n个字符插入

 (4)string& replace (size_t pos, size_t len, size_t n, char c);

替换n个相同字符

8.swap

交换两个字符串

思考:

明明全局swap也可以达到交换的效果,那string里面也实现一个swap的成员函数有必要吗?? 

 

综上,要尽量使用成员函数的swap 

2.5 string类对象的操作(operations)

1、c_str(重点)

 返回一个指向C类型的字符串指针,下面介绍他的用处:

     我们可以观察到,s1.c_str()返回的其实是一个char*指针,但是为什么打印出来的不是地址呢??因为cout可以自动识别类型,对于char*类型的指针他会把它当成是字符串去处理,只要指针不是char*类型的,都会当成打印地址。

     我们会发现,当我们尾插‘\0’后再插入一些字符,打印出来的结果就不一样了,因为对于c语言来说,字符串默认是读取到\0停止,但是对于string来说,读取多少是取决于他的成员变size!!

如果string类我们想用C语言的方法处理文件,就可以用c_str 

 2、find

 找一个字符里的子串是否存在,如果存在,返回对应的第一个字符的下标,如果不存在,就会返回string::npos。

(1)(3)(4)版本差不多,区别是一个是找string类,一个是找常量字符串,一个是找字符。pos的缺省值0,不传的话就是从头开始遍历往后找,我们也可以通过pos来缩小查找的范围。

(2)版本就是 找常量字符串从pos位置开始的n个字符

 3、refind

 和find的区别就是默认是pos开始从后往前找

 4.substr

 从pos位置开始截取len个字符返回,不传len就是默认全部返回(经常和find以及rfind配合使用)

比如我们想打印出.com,我们可以先用find去定位'.'然后再打印

 如果我们不知道几个字符,s1.size()-pos刚好是剩下所有的字符,或者就干脆不传,这时候相当于就是把后面的全部打印完

有些时候要从后往前找(rfind)

 如果我们想打印中间怎么办??比如"http://www.cplusplus.com/reference/string/string/find/"我想打印出www.cplusplus.com

5.find_first_of

 找到第一个匹配的子串中任何一个字符的下标

 练习:将Please, replace the vowels in this sentence by asterisks中的abcdef全部换成*

6,find_last_of

找到最后一个与子字符串任意一个字符匹配的下标。其实可以理解从后往前找第一个

 7,find_first_not_of   / find_last_not_of

跟前两个类似,区别就是找第一个不匹配的和找最后一个不匹配的

2.6 string类对象的非成员函数

1,operator+ (string)

尽量少用,因为传值返回导致深拷贝效率很低

2,relational operators (string)

我们可以通过这些重载的操作符来比较字符串!! 

3,operator>>(string)和operator<< (string)

       值得注意的是,从c的字符串数组到c++的string类,原先读取字符串是默认读取到\0,但是封装乘string类后他有了自己的size,所以会根据size去打印,因此是可以打印出\0的,但是>>还是跟之前的scanf一样,默认以换行或者是空格作为标识,如果我们想打印出有空格的字符串,是行不通的!!

 因此我们想要流插入有空格的字符串,就得用getline

4.getline

 注意要包含string的头文件

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构从入门到精通——算法的时间复杂度和空间复杂度

算法的时间复杂度和空间复杂度 前言一、算法效率1.1 如何衡量一个算法的好坏1.2 算法的复杂度 二、时间复杂度2.1 时间复杂度的概念2.2 大O的渐进表示法2.3常见时间复杂度计算举例2.4等差数列计算公式2.5等比数列计算方法 三、空间复杂度四、 常见复杂度对比五、 复杂度的oj练习…

Data Leakage and Evaluation Issues inMicro-Expression Analysis 阅读笔记

IEEE Transactions on Affective Computing上的一篇文章&#xff0c;做微表情识别&#xff0c;阅读完做个笔记。本文讨论了Data Leakage对模型准确度评估的影响&#xff0c;及如何融合多个微表情数据集&#xff0c;从而提升模型的准确度。工作量非常饱满&#xff0c;很认真&…

蓝桥杯算法 一.

分析&#xff1a; 本题记录&#xff1a;m个数&#xff0c;异或运算和为0&#xff0c;则相加为偶数&#xff0c;后手获胜。 分析&#xff1a; 369*99<36500&#xff0c;369*100>36500。 注意&#xff1a;前缀和和后缀和问题

C++数据结构与算法——二叉搜索树的属性

C第二阶段——数据结构和算法&#xff0c;之前学过一点点数据结构&#xff0c;当时是基于Python来学习的&#xff0c;现在基于C查漏补缺&#xff0c;尤其是树的部分。这一部分计划一个月&#xff0c;主要利用代码随想录来学习&#xff0c;刷题使用力扣网站&#xff0c;不定时更…

获取PDF中的布局信息——如何获取段落

PDF解析是极其复杂的问题。不可能靠一个工具解决全部问题&#xff0c;尤其是五花八门&#xff0c;格式不统一的PDF文件。除非有钞能力。如果没有那就看看可以分为哪些问题。 提取文本内容&#xff0c;提取表格内容&#xff0c;提取图片。我认为这些应该是分开做的事情。python有…

[VNCTF2024]-PWN:preinit解析(逆向花指令,绕过strcmp,函数修改,机器码)

查看保护&#xff1a; 查看ida&#xff1a; 这边其实看反汇编没啥大作用&#xff0c;需要自己动调。 但是前面的绕过strcmp还是要看一下的。 解题&#xff1a; 这里是用linux自带的产生随机数的文件urandom来产生一个随机密码&#xff0c;然后让我们输入密码&#xff0c;用st…

msvcr120.dll丢失的解决办法的详细步骤,简单有效的解决msvcr120.dll丢失

当我们遇到电脑提示msvcr120.dll文件不见了时&#xff0c;无需过分紧张&#xff0c;因为这个问题有众多解决方案可供我们选择。今天我特意准备了一些应对这一问题的经验分享&#xff0c;并给大家带来一些独到的解决思路。让我们一起来探讨吧&#xff01; 一、先了解一下什么是m…

Swagger3 使用详解

Swagger3 使用详解 一、简介1 引入依赖2 开启注解3 增加一个测试接口4 启动服务报错1.5 重新启动6 打开地址&#xff1a;http://localhost:8093/swagger-ui/index.html 二、Swagger的注解1.注解Api和ApiOperation2.注解ApiModel和ApiModelProperty3.注解ApiImplicitParams和Api…

Huggingface初上手即ERNIE-gram句子相似性实战

大模型如火如荼的今天&#xff0c;不学点语言模型&#xff08;LM&#xff09;相关的技术实在是说不过去了。只不过由于过往项目用到LM较少&#xff0c;所以学习也主要停留在直面——动眼不动手的水平。Huggingface&#xff08;HF&#xff09;也是现在搞LM离不开的工具了。 出于…

k8s pv与pvc理解与实践

参考文章&#xff1a; https://blog.csdn.net/qq_41337034/article/details/117220475 一、 pv/pvc简述 Pv是指PersistentVolume&#xff0c;中文含义是持久化存储卷是对底层的共享存储的一种抽象&#xff0c;Pv由管理员进行配置和创建&#xff0c;只要包含存储能力&#xff…

Rocky Linux 安装部署 Zabbix 6.4

一、Zabbix的简介 Zabbix是一种开源的企业级监控解决方案&#xff0c;用于实时监测服务器、网络设备和应用程序的性能和可用性。它提供了强大的数据收集、处理和可视化功能&#xff0c;同时支持事件触发、报警通知和自动化任务等功能。Zabbix易于安装和配置&#xff0c;支持跨平…

论文阅读:2020GhostNet华为轻量化网络

创新&#xff1a;&#xff08;1&#xff09;对卷积进行改进&#xff08;2&#xff09;加残差连接 1、Ghost Module 1、利用1x1卷积获得输入特征的必要特征浓缩。利用1x1卷积对我们输入进来的特征图进行跨通道的特征提取&#xff0c;进行通道的压缩&#xff0c;获得一个特征浓…

C语言第三十三弹---动态内存管理(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 动态内存管理 1、为什么要有动态内存分配 2、malloc和free 2.1、malloc 2.2、free 3、calloc和realloc 3.1、calloc 3.2、realloc 4、常见的动态内存的错…

阿里云短信验证笔记

1.了解阿里云的权限操作 进入AccessKey管理 选择子用户 创建用户组和用户 先创建用户组&#xff0c;建好再进行权限分配 添加短信管理权限 创建用户 创建好后的id和密码在此处下载可以得到 2.开通阿里云短信服务 进行申请&#xff0c;配置短信模板 阿里云短信API文档 短信服务…

MySQL 逗号分隔查询--find_in_set()函数

业务场景&#xff1a; 在使用MySQL的时候&#xff0c;可能的某个字段存储的是一个英文逗号分割的字符串&#xff08;这里我们不讨论表设计的合理性&#xff09;&#xff0c;如图所示&#xff1a; 我们在查询的时候需要匹配逗号分割中的某个字符串&#xff0c;该怎么查询呢&am…

地图可视化绘制 | R-ggplot2 NC地图文件可视化

在推出两期数据分享之后&#xff0c;获取数据的小伙伴们也知道&#xff0c;数据格式都是NetCDF(nc) 格式网格数据&#xff0c;虽然我在推文分享中说明使用Python、R或者GIS类软件都是可以进行 处理和可视化绘制的&#xff0c;但是&#xff0c;还是有小伙伴咨询使用编程软件Pyth…

浅谈mysql mvcc

目录 前言 mvcc 是如何工作的&#xff1f; 数据的更新 前言 mvcc 与一个事物的隔离级别有关&#xff0c;未提交读永远读的是当前值&#xff0c;串行化是通过加锁实现&#xff0c;这两种隔离级别都与mvcc 没有任何关系。只要一提到mvcc应该想到的是读提交以及可重复读&#…

Spring八股 常见面试题

什么是Spring Bean 简单来说&#xff0c;Bean 代指的就是那些被 IoC 容器所管理的对象。我们需要告诉 IoC 容器帮助我们管理哪些对象&#xff0c;这个是通过配置元数据来定义的。配置元数据可以是 XML 文件、注解或者 Java 配置类。 将一个类声明为 Bean 的注解有哪些? Com…

【buuctf-gakki】

binwalk 查看图片&#xff0c;发现有 rar 文件&#xff0c;提取后如上图所示&#xff08;flag.txt为已经解压后出来的&#xff09;其中这个 rar 需要用 archpr爆破一下 打开后一个 flag.txt 一堆杂乱无章的字符&#xff0c;需要用到 python 脚本进行词频统计&#xff0c;我们…

Vue3 在SCSS中使用v-bind

template 先创建一个通用的页面结构 <template><div class"v-bubble-bg"></div> </template>js 在JS中先对需要用的数据进行定义&#xff1a; 可以是参数&#xff0c;也可以是data <script setup>const props defineProps({bgCol…