• C++ STL---string类用法


    目录

    1.构造

    2.容量

      (1)reserve使用及性质验证

        [1]扩容机制验证

        [2]扩容机制总结

      (2)resize使用及性质验证

    3.迭代器

    4.元素访问

    5.修改

    6.特殊操作

    7.string类的输入输出

      (1)支持cin和cout

      (2)在oj中的使用


            string类是C++STL中的序列形容器之一,它是动态类型的顺序表,只能存储char类型的字符。使用时需要包含头文件 "string"。

            string类主中的操作主要分为六大模块,构造、容量、元素访问、迭代器、修改、特殊操作。

    在本文中只讲解最常用的一些函数(因为它的函数实在太多了)。

            本文由于是介绍string类函数的用法,因此绝大部分内容都在代码中,文字叙述较少。(本文代码均在win10系统下的vs2019验证)

    1.构造

            string类对象常用的五种构造如下表:

    函数名称功能说明
    string()构造空的string对象,即空字符串
    string(const char* s)用C语言中的字符串构造string类对象
    string(size_t n,char c)string类对象中包含n个字符c
    string(const string& s)拷贝构造函数
    string(const char* s,size_t n)用C语言字符串的前n个字符构造string类对象

            代码一:在此代码中展示以上五种构造函数的使用方法。

            代码一:可以看到string类对象可以直接用cin>>来接收键盘输入的字符串,也可以用cout直接输出string类对象的内容。但是使用cin>>直接接收时,碰到空格会停止接收。

    1. //代码一
    2. #include "iostream"
    3. #include "string"
    4. using namespace std;
    5. void Test() {
    6. string s1;//构造空的string对象,即空字符串
    7. const char* s = "abcde";//C语言中的字符串
    8. string s2(s);//用C_string来构造string类对象
    9. string s3(10, 'A');//string类对象中包含10个字符‘A’
    10. string s4(s3);//拷贝构造函数
    11. string s5(s, 3);//用C语言字符串的前3个字符构造string类对象
    12. cin >> s5;
    13. cout << s5;
    14. }
    15. int main() {
    16. Test();
    17. }

    2.容量

            string类在容量这一模块中的操作较多,其中有一部分的函数还具有自己的特性需要进行分析。常用的容量相关的操作如下表:

    函数名称功能说明
    size()返回字符串有效字符长度
    length()返回字符串有效字符长度
    capacity()返回总空间的大小
    empty()检测string类对象是否是空的,是返回true,否则返回false
    clear()清空有效字符
    reserve(size_t n)为string类对象总空间大小进行重新设置
    resize(size_t n,char c)将有效字符个数修改为参数中的n个,多出的空间用参数中的c填充,如果没有第二个参数,填充为0

            注意:总空间是指string对象中一共有多少空间(容量)可以用来存储char类型数据,有效元素个数是指在总空间中有多少空间都存放了有效的char类型数据。

            前五个函数较为简单,利用下述代码来展示用法:

            代码二:s1.capacity() == 15 注意这个15,下面这个是要讲一讲的。

    1. //代码二
    2. #include "iostream"
    3. #include "string"
    4. using namespace std;
    5. void Test() {
    6. string s1("abcde");
    7. cout << s1.size() << endl;//输出 5
    8. cout << s1.length() << endl;//输出 5
    9. cout << s1.capacity() << endl;//输出 15
    10. cout << s1.empty() << endl;//输出 0
    11. s1.clear();
    12. cout << s1.empty() << endl;//输出 1
    13. }
    14. int main() {
    15. Test();
    16. }

      (1)reserve使用及性质验证

            reserve(size_t n)这个函数的作用是根据参数n来对string类对象的容量进行合适的改变(增长或缩小)。这个合适的就很有意思了,因为并不是n等于多少,它就一定把容量变成多少。

            首先来看一下string类的大小,如代码三:

            代码三:string类的大小是28,那么string类中都有什么?答案是:一个char*类型的变量,一个size_t类型的size,一个size_t类型的capacity,并且还维护了一个大小是16的数组(但在我的测试中发现,如果string类对象的字符数小于等于15个,容量是15。当字符数等于16时,容量变成31,发生扩容,所以推测第16个字节应该是为了存储'\0')。

    1. //代码三
    2. #include "iostream"
    3. #include "string"
    4. using namespace std;
    5. int main() {
    6. cout << sizeof(string) << endl;//输出 28
    7. }

        [1]扩容机制验证

            用下面两段代码验证 reserve(size_t n) 合适的扩容机制;

            代码四:当初始字符串中元素个数小于等于15时的扩容机制。

            这段代码较长,但要仔细看完。此时字符串长度是10,可以看到,扩容后的容量并不总是和传递的实参相等,通常情况下是要大一些的。

            还有一个现象是,在将参数n缩小后,发现刚开始的时候容量依然保持70不变,当n缩小到15后,容量变为15。当n的大小小于字符串长度后,容量保持不变,一直是15。

            同时观察扩容的容量大小增加情况:31 -> 47 -> 70。(说明vs2019中大概是按1.5倍的方式递增)

    1. //代码四
    2. #include "iostream"
    3. #include "string"
    4. using namespace std;
    5. void Test() {
    6. string s1("abcde12345");
    7. s1.reserve(20);
    8. cout << s1.size() << endl;//10
    9. cout << s1.capacity() << endl;//31
    10. s1.reserve(30);
    11. cout << s1.size() << endl;//10
    12. cout << s1.capacity() << endl;//31
    13. s1.reserve(40);
    14. cout << s1.size() << endl;//10
    15. cout << s1.capacity() << endl;//47
    16. s1.reserve(50);
    17. cout << s1.size() << endl;//10
    18. cout << s1.capacity() << endl;//70
    19. s1.reserve(60);
    20. cout << s1.size() << endl;//10
    21. cout << s1.capacity() << endl;//70
    22. s1.reserve(50);
    23. cout << s1.size() << endl;//10
    24. cout << s1.capacity() << endl;//70
    25. s1.reserve(40);
    26. cout << s1.size() << endl;//10
    27. cout << s1.capacity() << endl;//70
    28. s1.reserve(30);
    29. cout << s1.size() << endl;//10
    30. cout << s1.capacity() << endl;//70
    31. s1.reserve(20);
    32. cout << s1.size() << endl;//10
    33. cout << s1.capacity() << endl;//70
    34. s1.reserve(15);
    35. cout << s1.size() << endl;//10
    36. cout << s1.capacity() << endl;//15
    37. s1.reserve(12);
    38. cout << s1.size() << endl;//10
    39. cout << s1.capacity() << endl;//15
    40. s1.reserve(9);
    41. cout << s1.size() << endl;//10
    42. cout << s1.capacity() << endl;//15
    43. }
    44. int main() {
    45. Test();
    46. }

            代码五:当初始字符串中元素个数大于15时的扩容机制。

            此时字符串长度是0。可以看到,在n逐渐增加的过程中,在vs2019中也基本按照1.5倍大小扩容。但是在这一次缩小到15后,容量并没有变小。下面来总结一下它的性质。

    1. //代码五
    2. #include "iostream"
    3. #include "string"
    4. using namespace std;
    5. void Test() {
    6. string s1("abcde12345abcde12345");
    7. s1.reserve(20);
    8. cout << s1.size() << endl;//20
    9. cout << s1.capacity() << endl;//31
    10. s1.reserve(30);
    11. cout << s1.size() << endl;//20
    12. cout << s1.capacity() << endl;//31
    13. s1.reserve(40);
    14. cout << s1.size() << endl;//20
    15. cout << s1.capacity() << endl;//47
    16. s1.reserve(50);
    17. cout << s1.size() << endl;//20
    18. cout << s1.capacity() << endl;//70
    19. s1.reserve(60);
    20. cout << s1.size() << endl;//20
    21. cout << s1.capacity() << endl;//70
    22. s1.reserve(50);
    23. cout << s1.size() << endl;//20
    24. cout << s1.capacity() << endl;//70
    25. s1.reserve(40);
    26. cout << s1.size() << endl;//20
    27. cout << s1.capacity() << endl;//70
    28. s1.reserve(30);
    29. cout << s1.size() << endl;//20
    30. cout << s1.capacity() << endl;//70
    31. s1.reserve(20);
    32. cout << s1.size() << endl;//20
    33. cout << s1.capacity() << endl;//70
    34. s1.reserve(15);
    35. cout << s1.size() << endl;//20
    36. cout << s1.capacity() << endl;//70
    37. s1.reserve(12);
    38. cout << s1.size() << endl;//20
    39. cout << s1.capacity() << endl;//70
    40. s1.reserve(9);
    41. cout << s1.size() << endl;//20
    42. cout << s1.capacity() << endl;//70
    43. }
    44. int main() {
    45. Test();
    46. }

        [2]扩容机制总结

            1. reserve(size_t n) 不会改变有效元素的个数。(这就是为什么代码五中当n变为15时,容量不变,要是容量变成15,有效元素不就变少了吗?)

            2.reserve(size_t newcapacity),假设当前容量是 oldcapacity。分情况总结:

            newcapacity > oldcapacity:reserve函数扩容。(在vs2019中大概按照1.5倍扩容)

            newcapacity < oldcapacity:<1>字符串有效长度 <= 15,将容量缩小为15。<2>字符串有效长度 > 15,容量保持不变。

            有没有发现这几条性质都要围绕15这个数字展开,就是因为之前说过的,string类对象内部存在一个定长char类型数组,可以存放15个有效字符。当字符串要扩容,就要去堆上申请空间。当空间要缩小时,很多时候并不能成功,原因就是,申请空间不容易,编译器为了防止缩小后又扩容浪费时间,所以一般情况下就不会缩小。

      (2)resize使用及性质验证

            上面的reserve只是用来管理容量,现在的resize是专门用来管理有效字符长度的。

            它有两种使用方法:1.resize(size_t n) 将有效元素字符修改成n个,多出的空间用'\0'填充。2.resize(size_t n,char c)将有效字符修改为n个,多处的空间用字符c填充。

            代码六:

    1. //代码六
    2. #include "iostream"
    3. #include "string"
    4. using namespace std;
    5. void Test() {
    6. string s1("123");
    7. s1.resize(10,'d');
    8. cout << s1.size() << endl;// 10
    9. s1.resize(2);
    10. cout << s1.size() << endl;// 2
    11. }
    12. int main() {
    13. Test();
    14. }

    3.迭代器

            在STL初级阶段,我们就把迭代器当作指针来使用即可。

    函数名称功能说明
    begin()返回string对象首字符的迭代器
    end()返回string对象最后一个字符下一个位置的迭代器
    rbegin()返回string对象最后一个字符的迭代器
    rend()返回string对象首字符前一个位置的迭代器

            下图是正反迭代器的示意图:

            代码七:既然把迭代器当作指针使用,自然是可以解引用的。

    1. //代码七
    2. #include "iostream"
    3. #include "string"
    4. using namespace std;
    5. void Test() {
    6. string s("abcde");
    7. auto it1 = s.begin();
    8. auto it2 = s.end();
    9. auto it3 = s.rbegin();
    10. auto it4 = s.end();
    11. while (it1 != it2) {
    12. cout << *it1 << " ";
    13. it1++;
    14. }
    15. }
    16. int main() {
    17. Test();//输出 a b c d e
    18. }

    4.元素访问

    函数名称功能说明
    operatoe[size_t pos]返回pos位置的字符,const string类对象调用
    at(szie_t pos)返回pos位置的字符
    范围for和数组中的范围for使用方法相同

            代码八:注意,当string对象用const修饰时,不可以使用 s[0]='k' 及类似的方式修改string对象。因为const对象是不可以修改的。

    1. //代码八
    2. #include "iostream"
    3. #include "string"
    4. using namespace std;
    5. void Test() {
    6. string s("abcde");
    7. cout << s[0] << endl;//输出 a
    8. cout << s.at(0) << endl;//输出 a
    9. for (auto& e : s) {
    10. cout << e;
    11. }//输出 abcde
    12. s[0] = 'k';
    13. }
    14. int main() {
    15. Test();
    16. }

    5.修改

    函数说明功能说明
    push_back(c)在string类对象后尾插字符c
    append(const char* s)在string类对象后追加字符串s
    append(size_t n,char c)在string类对象后追加n个字符c
    operator += c 或 str 或 string类对象在string类对象后追加字符c 或 字符串str 或 string类对象
    insert(iterator it,char c)在迭代器it的位置插入字符c
    insert(size_t pos,const char* s)在下标为pos的位置插入字符串s

    substr(size_t pos,size_t n)

    从string类对象的pos位置开始向后复制n个元素
    earse(size_t pos,size_t n)在string类对象从pos位置开始向后删除n个元素
    erase(iterator firest,iterator last)在string类对象中删除 [first,last) 之间的元素,注意区间是左闭右开,last位置不能删除。

            代码九:

    1. //代码九
    2. #include "iostream"
    3. #include "string"
    4. using namespace std;
    5. //插入
    6. void Test1() {
    7. string s1("123");
    8. string s2("89");
    9. const char* str = "56";
    10. s1.push_back('4');
    11. s1.append(str);
    12. s1.append(1, '7');
    13. s1 += s2;
    14. s1 += '0';
    15. cout << s1 << endl;//输出 1234567890
    16. const char* st = "bc";
    17. s1.insert(s1.begin(), 'a');
    18. s1.insert(1, st);
    19. cout << s1 << endl;//输出 abc1234567890
    20. }
    21. //复制子串与删除
    22. void Test2() {
    23. string s1("1234567");
    24. string s2 = s1.substr(0, 7);
    25. cout << s2 << endl;//输出 1234567
    26. s1.erase(0, 5);
    27. cout << s1 << endl;//输出 67
    28. s2.erase(s2.begin(), s2.end() - 3);
    29. cout << s2 << endl;//输出 567
    30. }
    31. int main() {
    32. Test1();
    33. Test2();
    34. }

    6.特殊操作

    函数说明功能说明

    find(char c,size_t pos)

    npos

    在string类对象从pos位置开始向后查找字符c,遇见第一个c返回下标,若没找到返回npos

    如果pos没有传递,默认从0下标开始查找

    find(const string& s,size_t pos)在string对象中从pos位置开始向后查找子串s出现的首个位置的下标,找不到返回npos
    rfind(char c,size_t pos)

    在string类对象从pos位置开始向前查找字符c,遇见第一个c返回下标,若没找到返回npos

    若pos没有传递,默认从尾元素开始

    c_str()返回一个指向正规C字符串的指针常量, 内容与本string串相同。(为了兼容C语言)
    atoi(const char* s)将字符串s转换为对应的整型变量
    sort(iterator firest,iterator last)把string类对象 [first,last) 之间的元素按大小排序

            代码十:在使用函数时,一定要特别注意参数究竟是普通类型还是const类型。

    1. //代码十
    2. #define _CRT_SECURE_NO_WARNINGS
    3. #include "iostream"
    4. #include "string"
    5. using namespace std;
    6. void Test() {
    7. string s1("1234");
    8. cout << s1.find('2', 0) << endl;//输出 1
    9. const string s2("23");
    10. cout << s1.find(s2, 0) << endl;//输出1
    11. //rfind与find区别仅是方向的区别,故不做演示
    12. const char* str = s1.c_str();
    13. int num1 = atoi(str);
    14. cout << num1 << endl;//输出 1234
    15. //也可以利用strcpy函数
    16. char ss[100];
    17. strcpy(ss, s1.c_str());
    18. int num2 = atoi(str);
    19. cout << num2 << endl;//输出 1234
    20. }
    21. int main() {
    22. Test();
    23. }

    7.string类的输入输出

      (1)支持cin和cout

            代码十一:使用cin接收时,遇到空格等空白字符和回车后停止接收。可以使用cout直接打印。

    1. //代码十一
    2. #include "iostream"
    3. #include "string"
    4. using namespace std;
    5. void Test() {
    6. string s;
    7. cin >> s;
    8. cout << s;
    9. }
    10. int main() {
    11. Test();
    12. }

      (2)在oj中的使用

            代码十二:在刷oj题中经常会遇到需要循环输入的题目,这时候就需要掌握相应的使用方法。

    1. //代码十二
    2. #include "iostream"
    3. #include "string"
    4. using namespace std;
    5. //循环接收单个单词并打印
    6. void Test1() {
    7. string word;
    8. while (cin >> word) {
    9. cout << word <
    10. }
    11. //截止输入 ctrl + z 回车
    12. }
    13. //循环接收带有空格的字符串并打印
    14. void Test2() {
    15. string words;
    16. while (getline(cin,words)) {
    17. cout << words << endl;
    18. }
    19. //截止输入 ctrl + z 回车
    20. }
    21. int main() {
    22. Test1();
    23. Test2();
    24. }

  • 相关阅读:
    Oracle中执行动态SQL
    linux关于cmake,makefile和gdb的使用
    MySQL系统数据库及常用工具指令介绍
    《测绘综合能力》真题易错本
    《计算机视觉基础知识蓝皮书》第7篇 模型优化方法及思路
    Mars3D三维可视化平台
    HDU_7149
    mybatis/mp批量插入非自增主键数据
    一篇文章让你学会K8s软件安装神器Helm
    如何在 PHP 中对密码进行哈希处理
  • 原文地址:https://blog.csdn.net/weixin_57761086/article/details/126532903