• 【C++】string介绍


    在这里插入图片描述

    本篇博客由 CSDN@先搞面包再谈爱 原创,转载请标注清楚,请勿抄袭。

    前言

    这篇博客讲的是STL中的string,作为一名C++程序猿,搞懂STL可是非常重要的。这篇博客就是介绍一些string类中比较常用的函数。但重要的不是这,因为记住所有的函数接口是不可能的,而且重要的函数接口就几个。重要的是让大家培养出查文档的习惯。

    string类中成员变量大概长这样,其实就是一个专门放字符串的顺序表,不过是库里面给你提供了一个现成的,不需要你自己去实现:

    在这里插入图片描述
    这里的size和可不是sizeof()中的那个理解了。size就是有效字符的个数。这里是hello这五个。
    上面的图留个印象,可以帮助了解string类。

    为什么学习string类?

    C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。不是很方便。

    C++中有string类来提供各种各样的函数接口,大大提高了办事效率。

    学了数据结构的同学,简单理解string类的话,其实就相当于是存放数据类型为char的顺序表。

    函数接口有很多,我这里给出相应的网站供大家参考:STL中string参考文档,里面是英文的,读起来比较费劲的话我也没什么办法。

    简单说几点,看不懂没关系,刚接触肯定不能完全理解的,我刚学的时候也不懂。

    1. 字符串是表示字符序列的类。
    2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器(容器就相当于是数据结构)的接口,但添加了专门用于操作单字节字符字符串的设计特性。
    3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
    4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
    typedef basic_string<char, char_traits, allocator> string;
    
    • 1
    1. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

    关于编码的东西,我直接给出大家一篇文章:刨根问底之——什么是编码?
    感兴趣的可以自己看看,不过重点不在这。
    要记住一点就是不同的编码中,一个字符可能占用不同的字节数。
    比如utf-8一个汉字占用3个字节,utf-16一个汉字占用2个字节,utf-32一个汉字占用4个字节。

    1. 不能操作多字节或者变长字符的序列。

    string类的常用接口说明

    只讲常用的接口,如果想要自己拓展,可以查看文档,

    string类对象的常见构造

    先看一下提供了几个构造:
    在这里插入图片描述

    总共七个构造,很多,但常用的就两三个。上面图中标上序号为1,2,4的记住就行。剩下的我待会给出用法,不需要记,等到写程序的时候需要某种功能的时候,去这里面查查就行。

    上例子:

    标号为1,2,4的。

    1 string();
    2 string (const string& str);
    4 string (const char* s);
    
    • 1
    • 2
    • 3

    在这里插入图片描述
    上面cout能直接打印string类对象,是因为库中也有重载operator<<函数。
    在这里插入图片描述

    标号为3的。

    string (const string& str, size_t pos, size_t len = npos);
    
    • 1

    这个函数功能是从string类对象str中的pos位置开始,往后len个长的字符作为某个string对象的初始值。
    在这里插入图片描述
    当len超过了能够初始化的有效字符长度或者没有给len值的时候会直接将pos后面全部作为初始化的内容。

    len过大:
    在这里插入图片描述

    未给len(len为缺省参数npos的时候):
    在这里插入图片描述
    npos的值为-1:
    在这里插入图片描述
    上面第一句话就说了npos是string类中的一个静态成员常量。固定为-1。

    当len以无符号整型size_t接收-1的时候,-1以补码的形式给了len,len会接收到一个非常大的数,有42亿多,就是二进制全1的情况(如果不懂去了解一下整数的存储规则),其实也就相当于是len过大的情况。因为基本不可能出现一个42亿字节大小的字符串,也就是4G左右的大小。

    标号为5的

    string (const char* s, size_t n);
    
    • 1

    这个函数是将常量字符串s中前n个字符作为对象初始化的值。
    可以这样用:
    在这里插入图片描述

    也可这样用:
    在这里插入图片描述
    这里的c_str后面会讲,这里先不说。

    标号为6的

    string (size_t n, char c);
    
    • 1

    这个函数将类对象的初始化字符串设置为n个字符c。
    在这里插入图片描述
    最后一个先不说,用到迭代器了,等会讲迭代器的时候再说。

    析构函数

    这个没啥讲的,自动调用的东西。

    赋值运算符重载

    在这里插入图片描述
    库里提供了三个,1,2重要,但挨个说一下。

    第一个和第二个放一块:

    string& operator= (const string& str);
    
    • 1
    string& operator= (const char* s);
    
    • 1

    上例子:
    在这里插入图片描述
    第三个:

    string& operator= (char c);
    
    • 1

    在这里插入图片描述

    再说个这个:
    在这里插入图片描述
    上面的几个初始化,都是调用的构造函数,只有最后一个赋值才是赋值运算符重载,初始化时不管用没用=,都不是调用赋值运算符重载。编译器做了优化。
    这个在我前面类和对象中的博客也讲了,不懂的可以看看:类和对象下,看explicit关键字那里就行
    最下面那个才是赋值运算符重载。中间蓝色框框住的是调用对应函数时,反汇编中函数的地址,右边绿色的是调用的函数。

    [ ] 重载

    这个[ ]的重载,对于字符串来说非常有用,可以向数组一样来访问字符串中的任意位置字符。

    在这里插入图片描述
    例子:

    访问任意位置:
    在这里插入图片描述

    遍历字符串
    在这里插入图片描述

    可以看到,库里面给了两个,还有一个const修饰的。
    意思就是假如用string创建出的const的对象,这个对象是不能被修改的,此时再使用[ ]重载就会调用const修饰的[ ]重载。
    来例子:
    在这里插入图片描述
    此时就只能用来访问了,也就是只读的。
    在这里插入图片描述

    [ ]会自动检查越界的情况。
    在这里插入图片描述

    还有一个能随机访问的。
    在这里插入图片描述

    看下例子:
    在这里插入图片描述
    不过用起来没有[ ]那么方便,这个也是提供了有const修饰的函数的。
    这个at和[ ]的区别就是,用at越界了之后是抛异常,[ ]是程序直接崩溃。

    可以看到上面那张图中,for循环中我用到了s1.size()这个函数,讲讲这个。

    size和length

    在这里插入图片描述
    库里提供了这两个。
    二者表示的是一个意思,只不过当初STL还没出的时候string已经出了,没出STL的时候用的是length表示string中有效字符的个数,后来除了STL之后,为了跟STL中其他的容器看齐才加的size,因为别的容器中都用的是size来表示元素个数,所以后来也给string中加上了这个size,用来表示有效字符个数。

    那么上面的那个例子就好理解了。s1.size()就相当于是字符串的长度。

    这里再把lenth给出:
    在这里插入图片描述
    一模一样。

    迭代器

    这个就要重点说了。

    先看长啥样:
    在这里插入图片描述

    其中stirng::iterator就是迭代器,s1.begin()就是迭代时的初始位置,s1.end()就是迭代的末尾位置,就相当于是首元素的地址和末尾有效元素的地址。

    这个用法是死的,毕竟语法就是这样,没法改,只能记住。

    迭代器用起来就像是指针,但是有的容器中iterator用的是指针,有的就不是。
    string和vector(顺序表)中的迭代器其实就是指针实现的。
    其他的就不是指针了。

    但是在string和vector中不习惯用iterator,因为可以直接用[ ]来访问元素,用iterator反而麻烦了。

    list/map/set…只能用iterator来访问,用法也是类似的。

    这里给出list(链表)中使用iterator的例子:
    在这里插入图片描述
    注意循环里面的 it++,我刚学的时候老是忘记。
    可以看到,我们用C语言写的链表可没有这样的功能,只能是next来找下一个节点。所以说C++学STL是很有必要的,会方便很多。

    上面的是正向的迭代器,还有反向的迭代器。
    叫做reverse_iterator,对应的头和尾也要改成rbegin和rend。
    在这里插入图片描述

    不仅有正反向还有const修饰的对象。
    在这里插入图片描述
    像上面的一样,也是const对象就调用的是const修饰的begin和end。
    把例子给出:
    正向+const
    在这里插入图片描述

    反向+const
    在这里插入图片描述

    如果觉得写迭代器比较麻烦的话,可以用auto来替代它,auto可以自动识别类型,这个在我的第一篇C++博客当中也是说了的,感兴趣的可以看看直接看最后的auto就行
    还是给例子:
    在这里插入图片描述
    这时候不管有多长的类型,都可以直接用auto来替换,写起来就比较方便了。

    提到auto了就说一下范围for。
    给例子:
    在这里插入图片描述

    看起来很🐂,其实范围for底层就是用迭代器来实现的。

    迫于vs2019下反汇编看起来二者相似度不大,但我又我没法展示出来更为相似的地方,但反汇编还是有些地方是相似的。
    在这里插入图片描述
    在这里插入图片描述
    用vs2013可能更好观察一点。

    迭代器就说到这。

    下面把前面的构造函数的最后一个演示一下:

    template <class InputIterator>
      string  (InputIterator first, InputIterator last);
    
    • 1
    • 2

    上面的InputIterator first和InputIterator last这两个,其实传的就是迭代器。
    在这里插入图片描述
    参数中 s1.begin() 和 s1.end() 可加可减。
    在这里插入图片描述在这里插入图片描述

    字符串追加

    有三种用法。
    第一种是push_back接口。
    第二种是append接口。
    第三种是+=接口。
    在这里插入图片描述

    挨个演示

    push_back
    在这里插入图片描述
    加单个字符
    在这里插入图片描述

    append
    在这里插入图片描述

    展示几个,其实和构造函数里的一模一样那几种情况一模一样。

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

    在这里插入图片描述

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

    这个和构造函数里面的那个很相似。
    正常情况:
    在这里插入图片描述
    sublen太大:
    在这里插入图片描述
    不给sublen:
    在这里插入图片描述

    string& append (const char* s)
    
    • 1

    在这里插入图片描述

    演示下迭代器,剩下的两个就不说了,跟构造函数里的用法一样。
    在这里插入图片描述

    然后就是+=了
    在这里插入图片描述
    这个用起来会比前两个方便很多。

    演示一下:
    在这里插入图片描述
    所以前两个方法只是为了演示一下,不建议用,真要用的话用+=就行,很方便。

    关于容量的函数

    前言部分,给了这张图:
    在这里插入图片描述
    capacity函数可以返回string对象的容量
    在这里插入图片描述

    预开空间reserve:
    在这里插入图片描述
    这个函数会将_capacity开到n(或者更大)。不会初始化string对象中的内容。
    最后开的_capacity的大小在不同的系统下是不一样的,vs下是这,g++下就不是这个了,会直接开为100。
    在这里插入图片描述
    里面啥也没放:
    在这里插入图片描述

    预开空间并初始化resize:

    在这里插入图片描述
    这个函数会将_capacity开到n,并且会将size的大小改为n,然后将前n个初始化为c,如果没给c,则初始化为0。
    在这里插入图片描述
    看以看到size变为了100,capacity变为了111。

    string的扩容机制在vs2019下有点怪:
    我用下面的代码进行string类对象的扩容观察:

    string s1;
    	cout << "init size::" << s1.size() << endl;
    	int capacity = s1.capacity();
    	cout << "init capacity::" << s1.capacity() << endl;
    	int count = 1000;
    	while (count)
    	{
    		s1 += 'h';
    		if (capacity != s1.capacity())
    		{
    			cout << "capacity changed ::" << s1.capacity() << endl;
    			capacity = s1.capacity();
    		}
    		count--;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在vs2019下:
    在这里插入图片描述
    可以看到大概是1.5倍的扩。
    而且如果用reserve的话,也不是给定的capacity值。
    在这里插入图片描述

    但是g++下就不一样了:
    同样的代码放到g++下:
    在这里插入图片描述
    运行:
    在这里插入图片描述
    得到的结果就是100,不是vs2019下的111。

    再看一下扩容的情况:
    在这里插入图片描述
    这里能看到,g++下是2倍的扩的。

    就是给大家演示一下,不同编译器下的结果可能是不一样的。

    再演示一下resize缩小size的情况:
    在这里插入图片描述
    上面resize(5),会将hello world!!!中的 world!!!全部劈掉,只剩下hello。
    在这里插入图片描述

    insert和erase

    先看inseret:
    在这里插入图片描述

    其实参数和上面的构造等非常相似,都跑不开string对象,常量字符串,单个字符。

    string& insert (size_t pos, const string& str);
    //插string对象
    
    string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);
    //插string对象的sobpos位置处的后sublen个字符
    
    string& insert (size_t pos, const char* s);
    //插常量字符串
    
    string& insert (size_t pos, const char* s, size_t n);
    //插常量字符串的前n个字符
    
    string& insert (size_t pos, size_t n, char c);
    //插n个字符c
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    上面的都是从pos位置插入,只不过是前插。
    放一块演示一下:
    在这里插入图片描述
    剩下的就不说了。

    然后是erase
    在这里插入图片描述
    就说第一个。
    从pos位置开始,删除npos个字符。

    默认情况下全删
    在这里插入图片描述
    给上参数:
    在这里插入图片描述

    insert和erase不建议多用,因为二者都可能存在挪动数据的情况,效率比较低。

    find

    在这里插入图片描述
    上面都是找字符(串),找到了返回下标,找不到返回npos。

    //从pos位置开始找str
    size_t find (const string& str, size_t pos = 0) const
    
    • 1
    • 2

    在这里插入图片描述

    //从pos位置开始找s
    size_t find (const char* s, size_t pos = 0) const;
    
    • 1
    • 2

    在这里插入图片描述

    //从pos位置开始找s的前n个字符,很鸡肋,不如第二个
    size_t find (const char* s, size_t pos, size_t n) const;
    
    • 1
    • 2

    在这里插入图片描述

    //从pos位置开始找字符c
    size_t find (char c, size_t pos = 0) const;
    
    • 1
    • 2

    在这里插入图片描述
    这里注意,找的永远是从pos位置往后的第一个c。

    给道例题:将下面字符串中空格替换为字符串:::

    hello world I am xxx
    
    • 1

    也就是换成:

    hello:::world:::I:::am:::xxx
    
    • 1

    第一种方法:结合前面的insert和erase

    在这里插入图片描述
    具体的就不讲了,注意画图理解。

    第二种方法:空间换时间
    在这里插入图片描述

    还有一个函数replace,听名字就是替换。

    replace

    在这里插入图片描述

    string& replace (size_t pos,  size_t len,  const string& str);
    
    • 1

    将string对象的pos位置往后的len个字符替换为str。

    给几个例子:
    0位置往后的2个字符换为tmp
    在这里插入图片描述
    0位置往后的3个字符换为tmp
    在这里插入图片描述
    0位置往后的1个字符换为tmp
    在这里插入图片描述
    0位置往后的2个字符换为tmp
    在这里插入图片描述

    然后也能用这个来搞前面那道例题了:
    在这里插入图片描述

    然后replace的就不讲了,感兴趣的自己可以查文档。

    但是如果想要找到最后一个单词呢?
    可以用rfind。

    c_str

    这个比较有用,就是将string类对象中的字符串以常量字符串的形式返回。也就是将C++中的类对象转换为C中的常量字符串。
    在这里插入图片描述
    其实二者没什么区别。区别都是C++和C的语法使用上的。
    在这里插入图片描述
    但是有一个场景要注意:
    C++中打印字符串是以字符串本身的size来决定打印多少的。
    而C中的打印是以’\0’为结束标志的。
    在这里插入图片描述
    就记住这个就行。

    rfind

    这个就是倒过来找的意思。reverse find。
    在这里插入图片描述

    然后就可以做上面的那道题了:
    在这里插入图片描述

    还是不细讲,用的时候查文档就行。

    find_first_of

    这个函数名字起得不好,应该叫find_any_of,这个函数用法是找到字符串里任意一个字符第一次出现的位置。
    在这里插入图片描述
    例子:
    在这里插入图片描述
    这里的2就是hello中的第一个 l 的下标。
    在这里插入图片描述
    在这里插入图片描述
    这里的用法是从第五个位置开始找,找到oI中的任意一个。

    find_first_not_of

    找到对象中第一个没有出现字符串中字符的位置。
    在这里插入图片描述

    在这里插入图片描述

    不细讲。

    find_last_of

    找到最后出现的。
    在这里插入图片描述
    其实就是倒着找。
    在这里插入图片描述

    substr

    这个函数是帮忙取出子串的。
    在这里插入图片描述

    直接给例子:
    在这里插入图片描述
    取出下标为3位置处的后4个字符

    再来个例子:将网址的协议、域名、后面的分开打印出来。

    网址都是死格式:

    protocal://hostname:portname/pathname/?search#hash
    就先找://,搞成字串
    再找/,搞成字串
    后面的再搞成字串就OK了

    我就以我现在所编写的网址为例:
    在这里插入图片描述

    getline

    还有一个比较重要的东西,就是getline,相当于C语言中的fgets。
    就是获取一行字符串。
    在这里插入图片描述
    有的同学可能会说为啥不直接用cin呢?
    看:
    在这里插入图片描述
    cin和scanf一样,遇到空格或者回车就会当成是字符串就结束了。

    而getline和fgets(gets)就是C++和C中能够读取包含空格字符串的两个函数。

    所以这里用getline就好了。
    在这里插入图片描述

    to_string

    这个也比较好用。

    将数字转成字符串。
    在这里插入图片描述
    这些所有参数的类型都能转换成string类型。
    在这里插入图片描述
    C语言中的atoi没有这个好用。

    还有字符串转数字的。
    在这里插入图片描述
    在这里插入图片描述

    总结

    剩下的没什么好讲的了,其实最常用的就是[] += 迭代器 size这几个常用点,剩下的留个印象就行,没必要全记住,其他的想用的时候忘了查文档就行。

    到此结束。。。

  • 相关阅读:
    黄菊华老师,Python毕业设计毕设辅导教程(4):Python 基础概念
    进程环境~
    视频前处理:时域滤波MCTF技术学习
    Go的unsafe.Pointer
    【数据结构】什么是线性表?
    心理健康数据集:mental_health_chatbot_dataset
    为什么要分库分表?
    HTTP/1.1协议的状态码
    03-appium环境配置和启动参数设置
    Github每日精选(第15期):Go 的快速 HTTP 包 fasthttp
  • 原文地址:https://blog.csdn.net/m0_62782700/article/details/130796914