【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程序节,快要来了,大家会怎么样过呢


相关文章
|
5月前
|
缓存 算法 程序员
C++STL底层原理:探秘标准模板库的内部机制
🌟蒋星熠Jaxonic带你深入STL底层:从容器内存管理到红黑树、哈希表,剖析迭代器、算法与分配器核心机制,揭秘C++标准库的高效设计哲学与性能优化实践。
C++STL底层原理:探秘标准模板库的内部机制
|
5月前
|
编解码 Java 开发者
Java String类的关键方法总结
以上总结了Java `String` 类最常见和重要功能性方法。每种操作都对应着日常编程任务,并且理解每种操作如何影响及处理 `Strings` 对于任何使用 Java 的开发者来说都至关重要。
378 5
|
编译器 C++ 容器
【c++丨STL】基于红黑树模拟实现set和map(附源码)
本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。
338 2
|
存储 算法 C++
【c++丨STL】map/multimap的使用
本文详细介绍了STL关联式容器中的`map`和`multimap`的使用方法。`map`基于红黑树实现,内部元素按键自动升序排列,存储键值对,支持通过键访问或修改值;而`multimap`允许存在重复键。文章从构造函数、迭代器、容量接口、元素访问接口、增删操作到其他操作接口全面解析了`map`的功能,并通过实例演示了如何用`map`统计字符串数组中各元素的出现次数。最后对比了`map`与`set`的区别,强调了`map`在处理键值关系时的优势。
669 73
|
9月前
|
对象存储 C++ 容器
c++的string一键介绍
这篇文章旨在帮助读者回忆如何使用string,并提醒注意事项。它不是一篇详细的功能介绍,而是一篇润色文章。先展示重载函数,如果该函数一笔不可带过,就先展示英文原档(附带翻译),最后展示代码实现与举例可以直接去看英文文档,也可以看本篇文章,但是更建议去看英文原档。那么废话少说直接开始进行挨个介绍。
181 3
|
9月前
|
存储 编译器 C语言
关于string的‘\0‘与string,vector构造特点,反迭代器与迭代器类等的讨论
你真的了解string的'\0'么?你知道创建一个string a("abcddddddddddddddddddddddddd", 16);这样的string对象要创建多少个对象么?你知道string与vector进行扩容时进行了怎么的操作么?你知道怎么求Vector 最大 最小值 索引 位置么?
237 0
|
缓存 安全 Java
《从头开始学java,一天一个知识点》之:字符串处理:String类的核心API
🌱 **《字符串处理:String类的核心API》一分钟速通!** 本文快速介绍Java中String类的3个高频API:`substring`、`indexOf`和`split`,并通过代码示例展示其用法。重点提示:`substring`的结束索引不包含该位置,`split`支持正则表达式。进一步探讨了String不可变性的高效设计原理及企业级编码规范,如避免使用`new String()`、拼接时使用`StringBuilder`等。最后通过互动解密游戏帮助读者巩固知识。 (上一篇:《多维数组与常见操作》 | 下一篇预告:《输入与输出:Scanner与System类》)
334 11
课时14:Java数据类型划分(初见String类)
课时14介绍Java数据类型,重点初见String类。通过三个范例讲解:观察String型变量、&quot;+&quot;操作符的使用问题及转义字符的应用。String不是基本数据类型而是引用类型,但使用方式类似基本类型。课程涵盖字符串连接、数学运算与字符串混合使用时的注意事项以及常用转义字符的用法。
362 9
|
存储 算法 C++
【c++丨STL】set/multiset的使用
本文深入解析了STL中的`set`和`multiset`容器,二者均为关联式容器,底层基于红黑树实现。`set`支持唯一性元素存储并自动排序,适用于高效查找场景;`multiset`允许重复元素。两者均具备O(logN)的插入、删除与查找复杂度。文章详细介绍了构造函数、迭代器、容量接口、增删操作(如`insert`、`erase`)、查找统计(如`find`、`count`)及`multiset`特有的区间操作(如`lower_bound`、`upper_bound`、`equal_range`)。最后预告了`map`容器的学习,其作为键值对存储的关联式容器,同样基于红黑树,具有高效操作特性。
543 3
|
存储 JavaScript Java
课时44:String类对象两种实例化方式比较
本次课程的主要讨论了两种处理模式在Java程序中的应用,直接赋值和构造方法实例化。此外,还讨论了字符串池的概念,指出在Java程序的底层,DOM提供了专门的字符串池,用于存储和查找字符串。 1.直接赋值的对象化模式 2.字符串池的概念 3.构造方法实例化
240 1