【C++初阶】STL-string的使用 ╰+哭是因爲堅強的太久メ 2024-03-31 14:23 9阅读 0赞 #### 文章目录 #### * 一.string初识 * * 1.STL简介 * * a.STL的组成 * b.STL和string的关系 * 2.basic\_string * 二.构造函数 * 三.三种遍历方式 * 四.容量相关的函数 * * 1.size() * 2.reserve()–调整容量 * 3.resize()–调整size * 五.字符串的增删查改 * * 1.assign * 2.replace * 3.find() * 4.substr() * 5.insert() * 6.相关应用 * * a.替换空格: * b.取出文件后缀: * 六.字符串操作函数 * * 1.c\_str * 2.getline() ## 一.string初识 ## ### 1.STL简介 ### #### a.STL的组成 #### > STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且 是一个包罗数据结构与算法的软件框架。 ![image-20221126230014736][] > 网上有句话说:“不懂STL,不要说你会C++”。STL是C++中的优秀作品,有了它的陪伴,许多底层的数据结构 以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。 #### b.STL和string的关系 #### 推荐一个学习C++的一个文档网站:C++文档说明,看文档也是一种工作必备能力哦 > 历史上,string出现的比STL出现的早,但是因为功能上string和STL中的容器很像,所以把string纳入到STL中。 ### 2.basic\_string ### > C++plusplus以后我用的时候就称为C++文档了,希望大家理解,C++文档中 ![image-20221126231347141][] ![image-20221126231533584][] 所以原理上,下面两种方式都是定义一个字符串: string str1; basic_string<char> str2; 那么是否还可以通过其他的类型来完成类模板的实例化?可以! ![image-20221126231658935][] > 上述有多种模板实例化的模板类,那和字符集编码有关。我们之所以用的最多的是string那是因为我们通常使用到的是utf-8字符集编码,所以我们一般使用basic\_string< char >存储utf-8字符组成的字符串; 如果我们使用的是utf-16字符集编码,我们继续使用basic\_string< char >存储utf-16组成的字符串就会出现乱码,所以我们应该使用basic\_string< char16\_t > ## 二.构造函数 ## > 构造函数的这几种构造方式要非常熟悉,因为string类的其他接口也有用到类似形式的构造参数,我称之为参数可变(个人叫法) <table> <thead> <tr> <th align="center">构造函数</th> <th align="center">说明</th> </tr> </thead> <tbody> <tr> <td align="center">string()</td> <td align="center">重要,无参构造</td> </tr> <tr> <td align="center">string (const char* s)</td> <td align="center">重要,常量字符串构造</td> </tr> <tr> <td align="center">string (const string& str)</td> <td align="center">重要,拷贝构造</td> </tr> <tr> <td align="center">string (size_t n, char c)</td> <td align="center">了解,n个c字符构造</td> </tr> <tr> <td align="center">string (const char* s, size_t n)</td> <td align="center">了解,s字符串前n个构造</td> </tr> <tr> <td align="center">string (const string& str, size_t pos, size_t len = npos)</td> <td align="center">了解,str对象的pos下标,跨度len的构造</td> </tr> <tr> <td align="center">string (InputIterator first, InputIterator last)</td> <td align="center">了解,迭代器相关</td> </tr> </tbody> </table> * ![image-20221126235648068][] > ps:string类的接口由100多个,但是我们学习的目标是:熟练掌握常用的20余个,其他的要用的时候翻阅文档查看学习使用即可. void test1() { string str1;//空字符串 string str2("hello");//"hello" string str3(str2);//"hello" string str4(5, 'x');//"xxxxx" string str5("XXYYZ", 4);//"XXYY" string str6(str5, 0, 2);//"XX" } ![image-20221127000801303][] ## 三.三种遍历方式 ## > 1.下标 + \[ \] > > 2.范围for:实际上auto是模仿其他语言的,底层实现也是迭代器 > > 3.迭代器:迭代器可能可能是指针,也有可能不是指针,但是它使用起来很像指针。(iterator具体实现得看后面了,这里会用就行,不必深究) void test2() { string str("123456"); //1. 下标+[] for (size_t i = 0; i < str.size(); ++i) { str[i]++; cout << str[i] << " "; } cout << endl; //2. 范围for for (auto& e : str) { e--; cout << e << " "; } cout << endl; //3. 迭代器 string::iterator it = str.begin(); while (it != str.end()) { (*it)++; cout << *it << " "; it++; } cout << endl; } > 既然第一种下标加方括号的形式既支持可读又可写,为什么我们还要学习迭代器呐? > > 那是因为迭代器是一种通用的访问形式,在string和vector中由于底层实现都是一种顺序表,而顺序表是支持下标加方括号的形式随机访问的,但是如果我没学到后面的list等,下标加方括号显然就不适用了,这个时候叠加器的优点才真正显示出来。 > > 那么接下来我将给大家介绍一下几种迭代器: > > 1. 正向迭代器和反向迭代器 > 2. const迭代器 > > 上面的迭代器都可以两两组合,比如正向非const迭代器,正向const迭代器等 * 反向迭代器 ![image-20221127195743771][] * const迭代器 ![image-20221127201155634][] > 这里的string::const\_iterator是不是使用起来不是那么方便,那么我们可以选择使用auto自动识别类型。 * 反向const迭代器 > 上面讲一下反向const迭代器这种类型名最长的组合形式: ![image-20221127201559731][] > 到了这里我们也了解到了什么是const的迭代器,什么是非const迭代器,那么为什么迭代器要s设计出const版本和非const版本呢?这是和它的功能有关,他必须要支持读和写的功能。 > > operate\[\]运算符重载函数其实设计出了const和非const两种版本 > > size()函数功能上只需要具备读,所以也就只设计出const版本 > > push\_back()函数功能上只需要具备写,所以也就只设计出非const版本 ![image-20221127202848934][] ## 四.容量相关的函数 ## <table> <thead> <tr> <th align="center">函数</th> <th align="center">说明</th> </tr> </thead> <tbody> <tr> <td align="center">size()</td> <td align="center">返回字符串有效字符长度</td> </tr> <tr> <td align="center">capacity()</td> <td align="center">返回空间总大小</td> </tr> <tr> <td align="center">clear()</td> <td align="center">清空有效字符</td> </tr> <tr> <td align="center">reserve(n)</td> <td align="center">仅改变capacity(),常用于扩容</td> </tr> <tr> <td align="center">resize(n)</td> <td align="center">改变size(),可能改变capacity(), 将有效字符的个数该成n个,多出的空间用字符c填充</td> </tr> <tr> <td align="center">empty()</td> <td align="center">检测字符串释放为空串,是返回true,否则返回false</td> </tr> </tbody> </table> ### 1.size() ### > 或许你学了许久C++都不知道为什么C++既有size()又有length(),它们的结果明明是一样的。 ![image-20221127203934579][] > 原因:string设计早于STL,STL有自己的一套,也就包含size() > > string有它的一套,也就是length(),作为STL的设计者把string加入到STL中的时候,为了向前兼容,就保留了原来的一套。 > > 那么我们究竟在使用的时候推荐使用size()还是length()呐?用之疑,肯定是优先选择size(),因为他能和后面的其他容器保持一定的统一性,字符串你能使用length(),但是对于二叉树这种结构能使用length()吗?显然不可以。 > > 另外通过上面的图的运行结果,我们也可以看出size()和length(),计算出的结果都是有效字符个数,是不包含斜杠铃的,因为‘\\0’是字符串结尾的标记字符 > > ps:vs下,capacity()计算的容量结果也没有包含‘\\0’ ### 2.reserve()–调整容量 ### void test6() { string str; size_t sz = str.capacity(); cout << "InitCapacity:"<< sz << endl; for (int i = 0; i < 100; i++) { str.push_back('c'); if (sz != str.capacity()) { sz = str.capacity(); cout << "capacity changed:" << sz << endl; } } } ![image-20221127205654865][] > 首先我们回顾一下:vs下,capacity()计算的容量结果是不包含‘\\0’的(包含还是不包含和具体实现有关),那我们可以到Linux下试一试: ![image-20221127210736375][] > 对比发现,vs下扩容倍数大概是1.5倍 > > linux的g++下,扩容倍数大概是2倍(这点上还是linux友好点) 那我为什么要和大家讲上面的这些东西呐?那是想和大家说明vs和Linux下的g++下,扩容是有代价的,所以如果我们在已知大概容量的情况下可以使用reserve() 提前开好适当大小的空间,从而减少扩容。 void test6() { string str; str.reserve(100); size_t sz = str.capacity(); cout << "InitCapacity:"<< sz << endl; for (int i = 0; i < 100; i++) { str.push_back('c'); if (sz != str.capacity()) { sz = str.capacity(); cout << "capacity changed:" << sz << endl; } } } ![image-20221127212116460][] 可以看到扩容次数明显减少了!!!! 但是在vs下有一个奇怪的现象,知道就好,不必深究: ![image-20221127212618671][] ### 3.resize()–调整size ### > 作用:将有效字符的个数该成n个,多出的空间用字符c填充 ![image-20221127213449826][] void test8() { string str("hello world"); //str.size()==11 str.capacity()==15 str.resize(5); cout << "情况1: " << endl; cout << str.size() << endl; cout << str.capacity() << endl; cout << str << endl << endl; str.resize(13,'a'); cout << "情况2: " << endl; cout << str.size() << endl; cout << str.capacity() << endl; cout << str << endl << endl; str.resize(20,'b'); cout << "情况3: " << endl; cout << str.size() << endl; cout << str.capacity() << endl; cout << str << endl << endl; } ![image-20221127215243270][] ## 五.字符串的增删查改 ## <table> <thead> <tr> <th align="center">函数</th> <th align="center">说明</th> </tr> </thead> <tbody> <tr> <td align="center">push_back()</td> <td align="center">头插</td> </tr> <tr> <td align="center">pop_back()</td> <td align="center">头删</td> </tr> <tr> <td align="center">insert()</td> <td align="center">在pos位置插入,插入部分的种类类似构造</td> </tr> <tr> <td align="center">erase()</td> <td align="center">在pos位置删除</td> </tr> <tr> <td align="center">opearator+=()</td> <td align="center">重要,在字符串后追加字符串str</td> </tr> <tr> <td align="center">assign()</td> <td align="center">清空后重新赋新值,赋值部分的种类类似构造</td> </tr> <tr> <td align="center">replace()</td> <td align="center">替换</td> </tr> <tr> <td align="center">find()</td> <td align="center">查找子串,返回下标</td> </tr> <tr> <td align="center">substr()</td> <td align="center">返回子串</td> </tr> </tbody> </table> ### 1.assign ### > 功能和赋值类似 string& assign (const string& str); 参数可变 ![image-20221202163645124][] 演示: void test2() { string str1 = "hello world"; string str2 = "bit"; str1.assign(str2);//"bit" str1.assign("C++");//"C++" str1.assign("C++", 0, 1);//"C" str1.assign(str2, 1, 1);//"i" str1.assign(3, 'x');//"xxx" cout << str1 << endl; } ### 2.replace ### > 功能:把从pos位置开始,替换跨度为span的字符串 > > ,替换为str2(可变) string& replace (size_t pos, size_t len, const string& str); 第三个参数可变 ![image-20221202164410177][] 演示: void test3() { string str1 = "hello world"; str1.replace(1, 3, "bit");//"hbito world" cout << str1 << endl; string str2 = "song"; str2.replace(0, 1, "yong", 0, 3);//"yonong" cout << str2 << endl; string str3 = "libai"; str3.replace(1, 2, 5, 'x');//"lxxxxxai" cout << str3 << endl; } ### 3.find() ### > 功能:从pos位置开始查找子串(字符串或字符),找到返回下标,没找到返回npos size_t find (const string& str, size_t pos = 0) const; ![image-20221202170601034][] ps:pos参数缺省,默认从0开始找(默认是全局查找) 说明缺省值:npos static const size_t npos = -1; 表面npos是-1,但是因为类型是size_t,无符号整数类型,所以实际上是非常大的数 ![image-20221202172050520][] void test5() { string str1 = "hello world C++"; size_t pos = str1.find(' ',11);//11,ps:下标为11是在d的后一个位置' ',也就是我们要找的‘ '的位置 pos = str1.find("ll");//2 pos = str1.find("world", 3);//6 cout << pos << endl; } ![image-20221202171532720][] ### 4.substr() ### > 功能:返回从pos位置,跨度为span的字符串 string substr (size_t pos = 0, size_t len = npos) const; ![image-20221202165657075][] ps:substr()函数是全缺省的,默认从0开始找(默认是全局查找),跨度为npos void test4() { string str1 = "hello world"; string str2=str1.substr();//"hello world" str2 = str1.substr(1);//"ello world" str2 = str1.substr(1, 1);//"e" cout << str2 << endl; } ### 5.insert() ### > 功能:从pos位置开始,插入字符串或字符 string& insert (size_t pos, const string& str); ![image-20221202171724907][] 第二个参数可变 演示: void test6() { string str1 = "hello world"; str1.insert(0, "song");//相当于头插 --"songhello world" str1.insert(str1.size(), "libai");//'\0'的位置就是str1.size(),他的下标看他前面有多少个字符--"songhello worldlibai" string str2 = "C++"; str1.insert(1, str2);//"sC++onghello world" cout << str1 << endl; } > 由数据结构我们感知到这insert类头插因为大量挪动数据,效率肯定不高~ ### 6.相关应用 ### #### a.替换空格: #### 方法1: void test7() { string str1 = "hello world C++ "; size_t pos = str1.find(' '); while(pos != string::npos) { str1.replace(pos, 1, "%20"); pos = str1.find(' ', pos + 3); } cout << str1 << endl; } 方法2:以空间换时间 void test8() { string str1 = "hello world C++ "; string ans; ans.reserve(str1.size()); for (int i = 0; i < str1.size(); i++) { if (str1[i] != ' ') ans += str1[i]; else ans += "%20"; } cout << ans << endl; } 变式:如果我要把“abcdeffcadgascg”这字符串中的‘a’和‘c’字符都换成“\*”,我再使用find就搞不定了,我得使用名字起的很怪的函数接口find\_first\_of() > 作用:在str1中查找出在str2字符串中出现的任意一个字符,返回下标 void test12() { string str1 = "abcdefccdefa"; size_t pos = str1.find_first_of("ac", 0); while (pos != string::npos) { str1.replace(pos, 1, "*"); pos = str1.find_first_of("ac", pos + 1); } cout << str1 << endl; } ![image-20221202201241483][] #### b.取出文件后缀: #### void test10() { //要求取出后缀 string str1 = "test.cpp"; size_t pos = str1.find('.'); if (pos != string::npos)//if找到了 { string ans = str1.substr(pos); cout << ans << endl; } } 但是如果像Linux中的一些文件后缀:.zip.tgz 这时候find()就搞不定了,我们得用rfind,倒着找,才能找到.tgz void test11() { string str1 = "test.zip.tgz"; size_t pos = str1.rfind('.'); if (pos != string::npos)//if找到了 { string ans = str1.substr(pos); cout << ans << endl; } } ## 六.字符串操作函数 ## ### 1.c\_str ### > 功能:返回string类成员变量中\_str的地址 const char* c_str() const; 一般在C和C++结合使用的时候能使用到,比如用C语言打开文件 void test9() { string file("test.cpp"); FILE* fp = fopen(file.c_str(), "r"); char ch = fgetc(fp); while (ch != EOF) { cout << ch; ch = fgetc(fp); } fclose(fp); } ### 2.getline() ### > 功能:cin会读取到键盘输入的数据,当读到空格或者换行的时候,会停止读,所以当我们要键盘输入的字符串是带有空格的时候,不能再使用cin,而是采用getline,这在oj题中经常出现! istream& getline (istream& is, string& str) oj题目作为实例: 题目链接:https://www.nowcoder.com/practice/8c949ea5f36f422594b306a2300315da?tpId=37&&tqId=21224&rp=5&ru=/activity/oj&qru=/ta/huawei/question-ranking #include <iostream> #include <string> using namespace std; int main() { string str; //cin>>str; getline(cin,str); size_t pos=str.rfind(' '); cout<<str.size()-(pos+1)<<endl; return 0; } 另外一个注意的点就是:左闭右开才是个数! 相减得到之间个数,左边的值计算在个数内,右边的值不计算在个数内 ![image-20221202203323395][] [image-20221126230014736]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/fb4f98ba0dd14141b6dc5c93894d4d84.png [image-20221126231347141]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/9614027416964408a5c83ca41636c351.png [image-20221126231533584]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/11513909ca3c4b26b20e1832643033b5.png [image-20221126231658935]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/6bb74ef97ad142c8968ea5fb37bd2576.png [image-20221126235648068]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/5edd77397a82438e8020ccef5f0eba62.png [image-20221127000801303]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/914fe2910435482ca1747d9f8555ac4b.png [image-20221127195743771]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/7f5e42a7b2384718b3323526f94fe48b.png [image-20221127201155634]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/a5491d174c9f42b592113f09cfb6e2a3.png [image-20221127201559731]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/404a8abc20a44e53a06682e70c014592.png [image-20221127202848934]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/f63c134cfcde4f7b88ae5ef4799040c2.png [image-20221127203934579]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/3ff2b4236b99449d958464cf3a04c975.png [image-20221127205654865]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/ecaaa453931a4131b1cb31b5e392ca69.png [image-20221127210736375]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/f42a31e2f93648debc44adbf1debae7f.png [image-20221127212116460]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/2fdfd28fa37f47888cee679f433e6dd7.png [image-20221127212618671]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/3063a7ac804d4335ac5d99dd4759a47a.png [image-20221127213449826]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/f96236760a564e4c81ab877670ca7b4e.png [image-20221127215243270]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/cdf2f4d7c78446a5a0cf857a4c4ecafd.png [image-20221202163645124]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/eba4ead6288d4fdeb9c0517a97b5cefa.png [image-20221202164410177]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/304241421c7243209c52897c30f4a0ab.png [image-20221202170601034]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/729c19eff6ec4832a253bf7a3f2ee1d1.png [image-20221202172050520]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/c268ff1f309d45e0a3ca7963df77255e.png [image-20221202171532720]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/d1341e0011874903bde0a37291736cc0.png [image-20221202165657075]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/eedcc423797046ccbc552ec54491abc0.png [image-20221202171724907]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/a5b3b1ea9bff4534b06d1ddfd54741c5.png [image-20221202201241483]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/c0d03521efbd43409899030e7b647014.png [image-20221202203323395]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/31/126a5ab41e434737ac93281f257c0994.png
相关 C++初阶 入门 *目录** C++关键字(C++98) 命名空间 C++输入输出 缺省参数 函数重载 引用 内联函数 auto关键字(C++11) 基于范围的for循环... 淡淡的烟草味﹌/ 2024年04月17日 20:08/ 0 赞/ 16 阅读
相关 c++模板初阶 前言 在我们学习c语言中,我们发现很多逻辑一样但函数的数据类型不一样,我们都需重新写,这样就有点代码冗余了。当来到了c++就可以很好的解决这一问题,运用模板。这个模板其实 逃离我推掉我的手/ 2024年04月01日 17:47/ 0 赞/ 25 阅读
相关 【C++初阶】C++内存管理 文章目录 一.C/C++内存分布图 二.new和delete内存管理 1.对于内置类型 2.对于自定义类型(重点) 超、凢脫俗/ 2024年04月01日 15:05/ 0 赞/ 14 阅读
相关 【C++初阶】STL-string的使用 文章目录 一.string初识 1.STL简介 a.STL的组成 b.STL和string的关系 ╰+哭是因爲堅強的太久メ/ 2024年03月31日 14:23/ 0 赞/ 10 阅读
相关 【C++初阶】:模板初阶 模板初阶 一.函数模板 1.简单使用 2.模板原理 3.函数模板的实例化 4.模板参数的匹配原则 二.类 短命女/ 2024年03月22日 19:12/ 0 赞/ 24 阅读
相关 【C++初阶】:模板进阶 模板进阶 一.非类型模板参数 二.模板的特化 1.概念 2.函数模板特化 3.类的特化 1.全特化 雨点打透心脏的1/2处/ 2024年03月18日 00:20/ 0 赞/ 45 阅读
相关 【C++】模板初阶 文章目录 一、泛型编程 二、函数模板 1.函数模板概念 2.函数模板格式 3.函数模板的实例化 三、类模板 本是古典 何须时尚/ 2021年09月09日 03:40/ 0 赞/ 295 阅读
还没有评论,来说两句吧...