【C++】STL —— String类不会怎么办? 看文档(万字详解)(下)

简介: 【C++】STL —— String类不会怎么办? 看文档(万字详解)(下)

七、Modifiers 修改


🎨追加


0a2653c851af460fa595bd959398a8f1.png


+=最好用也最常用,因为既可以追加字符、也可追加字符串 ,其实底层调用了append和push_back


void test_string7()
{
  string s("hello");
  s.push_back('-');
  s.push_back('-');
  s.append("world");
  cout << s << endl;
  string str("我来了");
  s += '@';//字符
  s += str;//字符串
  s += "JDG总冠军";
  cout << s << endl;
}

0a2653c851af460fa595bd959398a8f1.png

其中append的迭代器可以选择性的打印字符串内容(了解即可)


string s("hello");
string str("我来了");
s.append(++str.begin(), --str.end());
string copy(s.begin() + 5, s.end() -5);

2d65d23f6d4748949b924e4057485923.png

下面来研究尾插扩容容量变化 ——


void test_string8()
{
  string s;
  size_t sz = s.capacity();
  cout << "making s grow:\n";
  for (int i = 0; i < 100; ++i)
  {
  s.push_back('c');
  if (sz != s.capacity())
  {
    sz = s.capacity();
    cout << "capacity changed: " << sz << '\n';
  }
  }
}


我们可以看见在vs下增容,第一次是两倍,后面是1.5倍的增容


0a2653c851af460fa595bd959398a8f1.png


如果提前知道是多少空间,可以调用reserve预留空间,避免频繁增容的消耗


s.resize(1000 , 'x'); //开空间+初始化


2d65d23f6d4748949b924e4057485923.png


🎨插入和删除


🐋尽量少使用头部的插入和删除,因为要挪动,O(N)效率低


0a2653c851af460fa595bd959398a8f1.png


🤞1️⃣小练习:在字符串中空格的地方插入一个%


我的第一想法:遍历字符串,遇到' '的时候,直接插入


void test_string9()
{
  //在空格的地方插入一个%
  string str("JDG NB 总冠军");
  for (size_t i = 0; i < str.size(); ++i)
  {
  if (str[i] == ' ')
  {
    str.insert(i, "20%");
  }
  }
  cout << str << endl;
}


但这样有没有问题呢?


0a2653c851af460fa595bd959398a8f1.png


那怎么样处理比较好呢?只需要在插入的地方i额外的+3,


void test_string9()
{
  //在空格的地方插入一个%
  string str("JDG NB 总冠军");
  for (size_t i = 0; i < str.size(); ++i)
  {
  if (str[i] == ' ')
  {
    str.insert(i, "20%");
    i += 3;
  }
  }
  cout << str << endl;
}


2️⃣练习升级:把字符串中遇到的空格替换成20%


这就要引入erase:删除字符串中的字符


2d65d23f6d4748949b924e4057485923.png


void test_string9()
{
  //在空格的地方插入一个%
  string str("JDG NB 总冠军");
  for (size_t i = 0; i < str.size(); ++i)
  {
  if (str[i] == ' ')
  {
    str.insert(i, "20%");
    i += 3;
  }
  }
  //再把空格删除掉哦
  for (size_t i = 0; i < str.size(); ++i)
  {
  if (str[i] == ' ')
  {
    str.erase(i, 1);
  }
  }
  cout << str << endl;
}


ps:最好不要自行挪动数据,因为[]会检查下标位置必须要小于size的,如果真的要挪动,要resize一下,增加长度


也有一种空间换时间的方法:创建新的串遍历,效率O(N)


void test_string9()
{
  string str("JDG NB 总冠军");
  string newstr;
  for (size_t i = 0; i < str.size(); ++i)
  {
  if (str[i] != ' ')
  {
    newstr += str[i];
  }
  else
  {
    newstr += "20%";
  }
  }
  cout << newstr << endl;
}


八、String operations 字符串操作


打印字符串,都能打印,但意义不同 ——


0a2653c851af460fa595bd959398a8f1.png


🤞前者是string类的流插入运算符的重载,以对象size为准,size是多少打印多少

后者是以常量字符串对象\0为准,遇到\0就结束(符合c语言标准)


所以说\0不一定是结束标志,在string里会被忽视


主要作用还是与函数接口接合——


string file("test.txt");  
  FILE* fout = fopen(s.c_str(), "w");//打印文件


![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/631a160b51da4d7e9729bbeb05c5295d.png


🌍substr 子串

➰· 取当前串的一个子串


4cebaac233b3433da32a72337a77fc60.png


len:如果len比能取到的串长或使用缺省值npos,都是能取多少取多少


🌍查找 find & rfind

🌊 1. 从字符串pos位置从前向后找字符c/字符串,返回该字符在字符串中的位置,找不到就返回npos


6de278e6d6694ce5bb08e7e842b7e74b.png


🌊 2. 从字符串pos位置倒着找找字符c/字符串,返回该字符在字符串中的位置


8ec4f2997fb246878c34ecd6d122b7c6.png


💢小练习:取字符串的后缀


void test_string11()
{
  string str("test.cpp");
  //找出后缀
  size_t pos = str.rfind('.');//面对多后缀的最好用 rfind
  if (pos != string::npos)
  {
  //string buff = str.substr(pos, str.size() - pos);
  string buff = str.substr(pos);//因为是取到结束
  cout << buff << endl;
  }
}


但是这样写有没有说明问题呢?如果后缀是test.cpp.tar.zip呢?要取最后一个后缀


这样就要将find ——》 rfind,即可解决


💢解析出网址的这三个部分:协议 - 域名 - 资源


#include<iostream>
#include<string>
using namespace std;
int main()
{
  string url = "https://cplusplus.com/reference/string/string/substr/";
  size_t pos1 = url.find("://");
  if (pos1 == string::npos)
  {
  cout << "非法字符串" << endl;
  return;
  }
  //取协议
  string protocol = url.substr(0, pos1);
  cout << protocol << endl;
  size_t pos2 = url.find('/', pos1 + 3);
  if (pos1 == string::npos)
  {
  cout << "非法字符串" << endl;
  return;
  }
  string domain = url.substr(pos1 + 3, pos2 - pos1 - 3);//取域名
  cout << domain << endl;
  string uri = url.substr(pos2 + 1);//取资源
  cout << uri << endl;
}


💢find 和find_first_of 区别

0a2653c851af460fa595bd959398a8f1.png

find_first_of:只要出现要寻找的串里的任意字符都找出来


吐槽一下:find_first_of 更应该叫find_any_of,但我们要尊重语法


2d65d23f6d4748949b924e4057485923.png


九、非成员函数重载


💦流插入&流提取

注意注意:流插入和流提取都是以空格、回车作为结束标志的。这意味着如果想要输入一个字符串,最终可能只读入了一个单词,剩余的留在缓冲区里。


0a2653c851af460fa595bd959398a8f1.png


于是我们引入了getline,做题会遇到


💦getline


2d65d23f6d4748949b924e4057485923.png


遇到换行才结束


💢小练习:输出一个整数,表示输入字符串最后一个单词的长度


#include <iostream>
#include <string>
using namespace std;
int main() {
    string str;
    getline(cin,str);
    size_t pos = str.rfind(' ');
    if(pos != string::npos)
    {
        cout<<str.size()-pos-1<<endl;
    }
    else{
        cout<<str.size()<<endl;
    }
}


💦to_string


0a2653c851af460fa595bd959398a8f1.png

void test_string12()
{
  //整形转字符串
  int ival;
  double dval;
  cin >> ival >> dval;
  string istr = to_string(ival);
  string dstr = to_string(dval);
  cout << istr << endl;
  cout << dstr << endl;
  //字符串转整形
  istr = "9999";
  dstr = "9999.99";
  ival = stoi(istr);
  dval = stod(dstr);
}


重要的事情说三遍:不会的查文档,不会的查文档,不会的查文档!!

2d65d23f6d4748949b924e4057485923.png


十、 vs和g++下string结构的说明(了解)


🎉VS下string结构的说明:


string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间:


当字符串长度小于16时,使用内部固定的字符数组来存放

当字符串长度大于16时,从堆上开辟空间

union _Bxty
{ // storage for small buffer or pointer to larger one
  value_type _Buf[_BUF_SIZE];
  pointer _Ptr;
  char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;


这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。

其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量, 最后:还有一个指针做一些其他事情。

故总共占16+4+4+4=28个字节。


2d65d23f6d4748949b924e4057485923.png


🎉g++下string的结构

G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:


空间总大小

字符串有效长度

引用计数

指向堆空间的指针,用来存储字符串。

struct _Rep_base
{
  size_type _M_length;
  size_type _M_capacity;
  _Atomic_word _M_refcount;
};


📢写在最后

1024程序节,快要来了,大家会怎么样过呢


相关文章
|
16小时前
|
存储 编译器 C语言
从C语言到C++_11(string类的常用函数)力扣58和415(中)
从C语言到C++_11(string类的常用函数)力扣58和415
5 0
|
16小时前
|
存储 C语言 C++
从C语言到C++_11(string类的常用函数)力扣58和415(上)
从C语言到C++_11(string类的常用函数)力扣58和415
5 0
|
16小时前
|
算法 编译器 C语言
从C语言到C++⑩(第四章_模板初阶+STL简介)如何学习STL(下)
从C语言到C++⑩(第四章_模板初阶+STL简介)如何学习STL
4 0
|
4天前
|
存储 算法 搜索推荐
C++|STL简介-string-vector基础运用
C++|STL简介-string-vector基础运用
|
6天前
|
C语言 C++ 容器
C++ string类
C++ string类
9 0
|
6天前
|
设计模式 算法 C++
【C++】STL之迭代器介绍、原理、失效
【C++】STL之迭代器介绍、原理、失效
13 2
|
6天前
|
Java 编译器 ice
【Java开发指南 | 第十五篇】Java Character 类、String 类
【Java开发指南 | 第十五篇】Java Character 类、String 类
27 1
|
1天前
|
C++
【C++基础】类class
【C++基础】类class
9 1
|
1天前
|
安全 程序员 编译器
C++程序中的基类与派生类转换
C++程序中的基类与派生类转换
8 1
|
1天前
|
C++
C++程序中的类成员函数
C++程序中的类成员函数
7 1