string的使用和模拟实现
💓博主个人主页:不是笨小孩👀
⏩专栏分类:数据结构与算法👀 C++👀 刷题专栏👀 C语言👀
🚚代码仓库:笨小孩的代码库👀
⏩社区:不是笨小孩👀
🌹欢迎大家三连关注,一起学习,一起进步!!💓
string
- string的介绍
- 结构
- string的常用接口
- 构造和析构
- 遍历string
- size和length
- capacity
- resize和reserve
- 运算符重载
- push_back和append
- insert和erase
- 查找
- 截取字符串
- 获取字符指针
- 交换对象
- 特殊读取
string的介绍
string是一个类,可以看成是一个用模板写的顺序表,它的底层结构和顺序表基本是一样的,一个字符指针和一个表示存储数据的个数的size,还有一个表示容量大小的capacity,我们知道C++需要兼容C语言,所以它的字符串后面也是需要有一个‘\0’,的但是size和capacity的大小是不包含这个‘\0’的,因此实际的容量要比capacity大一个。
- 字符串是表示字符序列的类。
- 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
- string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型。
- string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数。
- 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
总结:
- string是表示字符串的字符串类。
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
- string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
- 不能操作多字节或者变长字符的序列。
结构
string的结构和顺序表很相似,一个存放字符的指针,还有一个标记个数的size,还有一个标记容量的capacity。
class
{private:char* _str;size_t _size;size_t _capacity;
}
string的常用接口
构造和析构
构造函数我们可以用个缺省参数,直接搞定无参和有参的情况,然后使用初始化列表把size和capacity给初始化一下,然后开个空间把参数拷贝过去就可以了。
析构函数的话很简单,直接释放空间即可,把其他值置一下0即可。
//构造
string(const char* s = ""): _size(strlen(s)), _capacity(_size)
{_str = new char[_capacity + 1];strcpy(_str, s);
}
//析构
~string()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}
遍历string
- 我们可以使用for循环来遍历
因为库里的string支持了[]运算符重载,我们可以就像遍历数组那样来遍历string。
int main()
{string s("123456");for (int i = 0; i < 6; i++){cout << s[i] << " ";}cout << endl;return 0;
}
因为它是传引用返回的,所以我们可读可写。它重载了一个const版本,所以当你传const对象时,就只能读不能写。
int main()
{string s("123456");for (int i = 0; i < 6; i++){s[i]++;}cout << s << endl;return 0;
}
- 使用迭代器遍历
迭代器我们可以理解为一个指针,然后需要一个范围,库里有几个函数,可以帮我们确定这个范围。
如果我们要正着遍历可以使用begin和end。
它的返回值是一个iterator的迭代器类型,然后这个类型在string类里面的,所以我们需要确定类域。
int main()
{string s("123456");string::iterator it = s.begin();while (it != s.end()){cout << *it << " ";it++;}cout << endl;return 0;
}
如果要倒着遍历,就需要使用了,rbegin和rend。
只不过他们的返回类型是reverse_iterator。
int main()
{string s("123456");string::reverse_iterator it = s.rbegin();while (it != s.rend()){cout << *it << " ";it++;}cout << endl;return 0;
}
这两对都是可读可写的,还剩两对前面带c的,是只能读,不能写。具体可戳标题详解。
- 使用范围for
int main()
{string s("123456");for (auto e : s){cout << e << " ";}cout << endl;return 0;
}
size和length
size和length的功能是一样的,都是返回字符串的长度,和C语言的strlen很相似。
int main()
{string s("123456");cout << s.size() << endl;cout << s.length() << endl;return 0;
}
capacity
capacity是返回已经开辟的容量的大小。
int main()
{string s("123456");cout << s.capacity() << endl;return 0;
}
模拟实现
const size_t size() const
{return _size;
}
const size_t capacity() const
{return _capacity;
}
resize和reserve
- resize
resize可以修改size的大小。
库里重载两个版本,resize分3个版本,它可能比capacity大,也可能在size和capacity中间,也有可能比size小。如果他比capacity大的话,会扩容,并且如果我们给字符参数,就会在我们原本的字符串后面全补我们给的那个字符,直到size变成我们设置的那个,我们没给字符的话,会补‘\0’,如果在size和capacity中间,不会扩容,但是还是会补充,如果比我们的size还小的话,就充当删除的功能了。
int main()
{string s("123456");s.resize(20);s.resize(20,'x');s.resize(10);s.resize(4);return 0;
}
- reserve
reserve可以设置capacity的大小,如果我们知道字符串多大的话,我们可以提前开好空间,因为让系统自己自动扩容的话,可能会频繁扩容,消耗太大了,所以我们在知道需要存储的字符串多大时可以提起开好空间。
int main()
{string s("123456");cout << s.capacity() << endl;s.reserve(100);cout << s.capacity() << endl;return 0;
}
模拟实现
void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}
void resize(size_t n, char c = '\0')
{if (n <= _size){_str[n] = '\0';_size = n;}else{reserve(n);size_t begin = _size;_size = n;while (begin < n){_str[begin] = c;begin++;}_size = n;_str[begin] = '\0';}
}
运算符重载
- 赋值运算符
也就是说我们可以直接用string对象或者字符串来给新的对象赋值。
int main()
{string s;s = "123456";string s1("xxx");s = s1;return 0;
}
- +和+=
从这两张图我们就可以看出来,+=是成员函数,而+是全局函数。我们需要尾插是可以用+=也可以用+,但是+的代价是很大的,我们尽量使用+=。
int main()
{string s("123");s += "456";s += '7';s = s + '8';return 0;
}
模拟实现
string& operator+=(const char s)
{push_back(s);return *this;
}string& operator+=(const char* s)
{append(s);return *this;
}
- 流插入和流提取
也就是说,我们可以直接使用cout和cin来对sting对象来进行输入和输出了。
模拟实现
- 流提取
我们可以一个字符一个字符的提取,因为_str为私有的,我们无法访问,我们也可以使用友元,但是我们这里一个字符一个字符的访问。
ostream& operator<<(ostream& out, const string& s)
{int end = s.size();for (int i = 0; i < end; i++){out << s[i];}return out;
}
- 流插入
流插入也一样,我们一个字符一个字符的读,当读到‘\n’或者空格就停止,但是cin和scanf都无法读空格和换行,这是就需要一个函数,cin.get(),它的功能就和C语言的getcahr很相似。
istream& operator>>(istream& in, string& s)
{s.clear();char ch = in.get();while (ch != ' ' && ch != '\n'){s += ch;ch = in.get();}return in;
}
int main()
{string s;cin >> s;cout << s << endl;return 0;
}
- 比较运算符
我们可以直接使用比较运算符来对sting对象和字符串等进行比较了。
模拟实现
char& operator[](size_t pos)
{assert(pos < _size);return _str[pos];
}const char& operator[](size_t pos) const
{assert(pos < _size);return _str[pos];
}string& operator+=(const char s)
{push_back(s);return *this;
}string& operator+=(const char* s)
{append(s);return *this;
}bool operator<(const string& s)
{return strcmp(_str, s._str) < 0;
}bool operator==(const string& s)
{return strcmp(_str, s._str) == 0;
}bool operator<=(const string& s)
{return *this<s||*this==s;
}bool operator>(const string& s)
{return !(*this<=s);
}bool operator>=(const string& s)
{return !(*this<s);
}bool operator!=(const string& s)
{return !(*this==s);
}
push_back和append
- push_back
尾插一个字符
- append
尾插一段字符串,库里面给的接口很多,我们都不是很常用,我们都喜欢用+=。
模拟实现
void push_back(const char ch)
{if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size++] = ch;_str[_size] = '\0';}
void append(const char* s)
{size_t len = strlen(s);if (_size + len > _capacity){reserve(_size+len);}strcpy(_str + _size, s);_size += len;
}
insert和erase
- insert
我们一般用的都是在某个位置插入一个字符或者字符串。
模拟实现
string& insert(size_t pos, char ch)
{assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}int end = _size;//必须强转,不然会类型提升,当pos=0时发生死循环while (end >= (int)pos){_str[end + 1] = _str[end];end--;}_str[pos] = ch;_size++;return *this;
}string& insert(size_t pos, const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}int end = _size;while (end >= (int)pos){_str[end + len] = _str[end];end--;}strncpy(_str + pos, str, len);_size += len;return *this;
}
- erase
删除某个位置的len个字符
模拟实现
earse我们需要提供一个npos的半缺省参数,如果不给的话就表示将pos以后得数据都删了。
nops是一个所有类都有的,所以我们可以定义为静态成员变量。
string& erase(size_t pos, size_t len = npos)
{assert(pos < _size);if (pos + len > _size || len == npos){_str[pos] = '\0';_size = pos;return *this;}else{size_t begin = pos + len;while (begin <= _size){_str[begin - len] = _str[begin];begin++;}_size -= len;return *this;}
}
public:const static int npos = -1;
查找
- find
我们可以用这个函数来查找字符串或者字符。
int main()
{string s("123456");int i = s.find("23");int j = s.find('5',2);int x = s.find("123", 2, 3);return 0;
}
- rfind
rfind也是查找只不过是从最后开始找。和find相似。
模拟实现
size_t find(char c, size_t pos = 0) const{assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == c){return i;}}return npos;}size_t find(const char* s, size_t pos = 0) const{size_t len = strlen(s);assert(pos + len < _size);char* str = strstr(_str + pos, s);if (str != nullptr){return str - _str;}return npos;}
截取字符串
substr
从pos位置开始提取len个字符,不给len直接取到最后。
模拟实现
string substr(size_t pos = 0, size_t len = npos) const
{string s;if (len == npos || pos + len > _size){size_t i = 0;for (i = pos; i <= _size; i++){s += _str[i];}return s;}else{size_t i = 0;for (i = pos; i < pos+len; i++){s += _str[i];}s += '\0';return s;}
}
获取字符指针
我们知道string是一个类,那我们想要得到底层的那个字符指针怎么获得呢?
我们可以使用c_str这个函数
int main()
{string s("123456");const char* str = s.c_str();return 0;
}
模拟实现
const char* c_str() const
{return _str;
}
交换对象
swap
对于string有一个专门的swap,可以用来交换两个string对象。
int main()
{string s("123456");string s1("12");s.swap(s1);cout << s << endl << s1 << endl;return 0;
}
模拟实现
void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}
特殊读取
我们知道cin和scanf一样,遇到空格或者换行都会提前结束,那我们需要读取的字符串包含空格或者换行怎么办呢,这时候就需要getline了。
int main()
{string s;getline(cin, s);cout << s;return 0;
}
我们也可以自己指定读取结束的字符。
今天的分享就到这里,感谢大家的关注和支持。
相关文章:
string的使用和模拟实现
💓博主个人主页:不是笨小孩👀 ⏩专栏分类:数据结构与算法👀 C👀 刷题专栏👀 C语言👀 🚚代码仓库:笨小孩的代码库👀 ⏩社区:不是笨小孩👀 🌹欢迎大…...
基础算法---区间合并
直接上题目,不废话! 题目 给定 n 个区间 [l,r],要求合并所有有交集的区间。 注意如果在端点处相交,也算有交集。 输出合并完成后的区间个数。 例如:[1,3] 和 [2,6] 可以合并为一个区间 [1,6]。 输入格式 第一行包含整数 n。 接下来 n 行&am…...
C++(day4)
思维导图 封装Mystring #include <iostream> #include<cstring>using namespace std;class Mystring{ public://无参构造函数Mystring():size(10){strnew char[size];strcpy(str,"");cout<<"无参构造函数"<<endl;}//有参构造函数…...
docker 部署 node.js(express) 服务
1、在 express 项目根目录下新增 Dockerfile 文件,内容如下: 创建服务容器的方法,可以根据自己的情况选择: 1、以下示例为宿主机没有安装 node 环境的写法; 2、先在本地构建包含 node 和 express 的基础镜像࿰…...
商城系统开发,如何确保用户数据的安全性?
确保用户数据的安全性是商城系统开发中至关重要的一项任务。随着数字化时代的到来,用户的个人信息和交易数据已成为黑客和不法分子的重要目标,因此保护用户数据的安全性对于商城系统的成功运营至关重要。在开发商城系统时,以下几个方面是确保…...
黑客必备工具Kali Linux,安装与使用教程全包含,从入门到精通,全网最详细全面的Kali Linux教程
Kali Linux是一个高级渗透测试和安全审计Linux发行版,目前可以说是网络安全人员的专用系统。 Kali Linux功能非常强大,能够进行信息取证、渗透测试、攻击WPA / WPA2保护的无线网络、离线破解哈希密码、将android、Java、C编写的程序反编译成代码等等&am…...
2024滴滴校招面试真题汇总及其讲解(二)
4.【基础题】HashMap了解吗?介绍一下它对应的线程安全版本。 HashMap 是 Java 中一种键值对映射的集合,它使用哈希表来存储键值对。HashMap 具有插入和删除元素效率高的优势,但不是线程安全的。 ConcurrentHashMap 是 Java 中一种线程安全的 HashMap,它使用分段锁来保证线…...
嵌入式-C语言中的if语句
目录 一.if语句介绍 二.案例实操 2.1C语言运行模板代码 2.2运行方法 2.3案例 一.if语句介绍 if判断语句是一种用于根据条件来进行条件分支的控制流语句。通过判断一个条件的真假来决定执行不同的代码块。if语句的基本语法如下:if (条件表达式) {// 如果条件为…...
组合数 rust解法
组合数。 编写函数,参数是两个非负整数n和m,返回组合数 C n m C_n^m Cnm,其中m≤n≤25。 例如,n25,m12时答案为5200300。 解法: fn c(n: u32, m: u32)->u64 {let m if m > n-m {n-m}else{m};le…...
【SpringMVC】自定义注解与AOP结合使用
目录 一、SpringMVC之自定义注解 1.1 Java注解简介 1.2 为什么要用注解 1.3 注解的分类 ⭐ 1.3.1 JDK基本注解 1.3.2 JDK元注解 1.3.3 自定义注解 1.4 自定义注解三种使用案例 1.4.1 案例一(获取类与方法上的注解值) 1.4.2 案例二࿰…...
MyEclipse 用tomcat部署SSM项目后,项目名称和当前项目不一致
MyEclipse 用tomcat部署SSM项目后,项目成功启动,但是访问所有接口报404 从这里可以看到,部署的项目名为accurate_sugar_control_yc_api,但实际我们项目名字应该为accurate_sugar_control_otc_api 解决办法 在本地找到项目的根目…...
来喽!!炒鸡详细的“数据在内存中的存储”真的来喽!
目录 1. 整数在内存中的存储 1.1 ⼆进制介绍 1.1.1 2进制转10进制 1.1.2 10进制转2进制 1.1.3 2进制转8进制 1.1.4 2进制转16进制 1.2 原码、反码、补码 2. ⼤⼩端字节序和字节序判断 2.1 什么是⼤⼩端? 2.2 为什么有⼤⼩端? 2.3 练习 …...
【面试经典150 | 双指针】验证回文串
文章目录 写在前面Tag题目来源题目解读解题思路方法一:筛选判断方法二:原地判断 知识回顾回文串双指针字符串操作 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更…… 专栏内容以分…...
sql存储引擎
-- 查询建表语句 --可以查看引擎 show create table account; -- 可以看到默认引擎 InnoDB ENGINEInnoDB -- 查看当前数据库支持得存储引擎 show engines ; # InnoDB 默认 存储引擎 # MyISAM sql早期默认 存储引擎 # MEMORY 存储在内存中 用来做临时表和缓存 存储引擎 …...
Visual Studio 2022安装SVN插件教程
1. 第一步:避免踩坑,超级重要!!!关闭Visual Studio 2022应用程序;(不然插件装不上,一直转圈!) 2.第二步:下载Visual Studio 2022版本对应的SVN插件…...
【PyCharm Community Edition】:串口开发
串口开发 安装模块:pyserial端口检查:uartDevice自定义文件:SerialMonitor.py导入自定义文件:SerialMonitor.py延伸阅读 安装模块:pyserial Pyserial 是 Python 中使用串口通信的一个第三方库,使用它可以方…...
亲测可用!!!Centos7安装chrome+chromedriver以便实现selenium自动化详细教程
网上很多教程都是在线安装chrome,这样安装了最新稳定的chrome,可惜我遇到chromdriver的版本跟上 chrome,为了早日实现在centos服务selenium自动化,不可能去等待 chromdriver 更新,只能 chrome进行降版本来离线安装。花…...
spring cloud、gradle、父子项目、微服务框架搭建---cloud gateway(十)
总目录 https://preparedata.blog.csdn.net/article/details/120062997 文章目录 总目录一、简介二、order、pay服务 配置context-path三、新建gateway网关服务(1) 启动类添加 SpringCloudApplication 即可(2) application.yml 配…...
AD22使用笔记+积累库
一、前言 使用AD9习惯了,但是需求逐渐上来了就不够用了,好多快捷的新功能要新版本软件才能用,所以升级使用AD22 目录 1.添加层之后中间层无法布线 2.新增快捷方式CtrlW布线,不用点图标了 二、环境 AD22 三、正文 1.添加层之…...
20230912在ubuntu18.04下使用pigz来提高tar命令压缩解压缩的速度
20230912在ubuntu18.04下使用pigz来提高tar命令压缩解压缩的速度 2023/9/15 22:19 https://blog.csdn.net/wb4916/article/details/128447298 20221226编译Toybrick的TB-RK3588X开发板的Android12系统2-SDK预处理 4、单线程压缩。 建议使用:pigz多线程压缩…...
python-xpath语法-爬取彼岸图4k高清动漫壁纸
安装 pip install lxml导入 from lxml import etreexpath使用路径表达式提取html文档中的元素或元素集,然后元素通过沿路径path或步steps来选取数据 XPath常用语法格式 表达式描述div选取div元素的所有子元素/div选取根元素divul//li选取ul元素下的所有li子元素…...
韩信点兵:求韩信一共有多少兵
任务描述 本关任务:求韩信一共有多少兵。 韩信有一队兵,他想知道有多少人,便让士兵排队报数。 按从 1 至5报数,最末一个士兵报的数为 1; 按从 1 至 6 报数,最末一个士兵报的数为 5; 按从 1 …...
10个简单但超级有用的Python装饰器
装饰器(Decorators)是Python中一种强大而灵活的功能,用于修改或增强函数或类的行为。装饰器本质上是一个函数,它接受另一个函数或类作为参数,并返回一个新的函数或类。它们通常用于在不修改原始代码的情况下添加额外的…...
DataGrip 2023 年下载、安装教程、亲测可用
文章目录 前言1. 下载2. 安装3、DataGrip 常用操作4 推荐阅读 前言 DataGrip 是 JetBrains 发布的多引擎数据库环境,支持 MySQL 和 PostgreSQL,Microsoft SQL Server 和 Oracle,Sybase,DB2,SQLite,还有 Hy…...
6.SpringEL与List,Map
SpringEL与List,Map 文章目录 SpringEL与List,Map介绍Spring EL以注解的形式Spring EL以XML的形式 介绍 使用SpEL与 Map 和 List 的工作方式与Java是完全一样的 //get map whete key MapA Value("#{testBean.map[MapA]}") private String mapA;//get first value …...
【Oracle】使用 SQL Developer 连接 Oracle 数据库
文章目录 前言一、准备工作1、安装 SQL Developer2、安装 Oracle 数据库 二、连接 Oracle 数据库1、打开 SQL Developer2、连接数据库3、访问数据库 三、SQL 开发功能1、SQL Worksheet2、对象浏览器3、数据库管理 四、总结 前言 SQL Developer 是 Oracle 官方推出的一款免费的…...
PostgreSQL 事务并发锁
文章目录 PostgreSQL 事务大家都知道的 ACID事务的基本使用保存点 PostgreSQL 并发并发问题MVCC PostgreSQL 锁机制表锁行锁 总结 PostgreSQL 事务 大家都知道的 ACID 在日常操作中,对于一组相关操作,通常要求要么都成功,要么都失败。在关系…...
CANoe-Model Editor无法修改ARXML文件的问题、E2E在SOME/IP通信中的使用问题
1、Model Editor无法修改ARXML文件的问题 在CANoe 15软件版本中,Communication Setup导入arxml文件后,可以在model editor中打开arxml并修改配置。关闭model editor后再打开,可以看到修改的配置被保存了。 但是,当我把arxml文件从Communication Setup中移除后,再导入。此…...
Conan安装第三方依赖库时SSL验证失败解决办法
背景 c跨平台项目使用conan进行三方库依赖管理是比较通用的方案,更换开发环境后突然发现conan无法安装三方库了,报错如下: zlib/1.2.12: Not found in local cache, looking in remotes... zlib/1.2.12: Trying with conan-center... ERROR…...
基于springboot+vue的大学生智能消费记账系统
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容:毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…...
如何自做网站/新闻头条最新消息
TC130:看懂蒙特卡洛积分(一) 概率分布变换与随机采样zhuanlan.zhihu.comTC130:游戏渲染进阶zhuanlan.zhihu.com概率论背景(1) 数学期望设 是随机变量, 是 的函数, .A. 如果 是离散型随机变量, 其分布列为 .随机变量 的数学期望定义为 .的数学期望定义…...
重庆博达建设集团股份有限公司网站/磁力棒
cmd中输入 netstat -ano 回车.可以查看本机开放的全部端口. 协议:分为TCP和UDP本地地址(Local Address):代表本机IP地址和打开的端口号外部地址(Foreign Address):远程计算机IP地址和端口号状态…...
淘宝做网站找我要发票/长沙百度网站优化
http真的很重要 缓存验证 三次握手 https为什么安全 什么是长链接 http2的信道复用为什么可以提高性能 浏览器输入URL后HTTP请求返回的完整过程 http的发展历史 http/0.9,只有一个命令GET,没有header等描述数据的信息,服务器发送完毕&…...
网站推广优化如何做/北京seo学校
拆书,读书,帮你选技术书。橡皮擦 “读” “选” 技术书。 打开任意一个购书网站都包含着大量的技术书籍,如何选到一本好技术书成了我们打工人的难题。 很早以前萌生过这样一个想法,如果有人帮我先读一下技术书籍,告诉…...
潜江资讯网找工作/抖音优化公司
直接指定字段insert into DB1.dbo.tableA(field1, field2) select fieldname1, fieldname2 from DB2.dbo.tableA...
网站制作怎么做语音搜索框/google app下载
最新版 Docker 安装 Nginx1、在Docker中查询Nginx2、选择最新版本安装3、查询Docker容器中的镜像4、启动Nginx5、查询是否启动Nginx镜像6、停止Nginx7、删除Nginx容器1、在Docker中查询Nginx [rootshiqi /]# docker search nginx #查询结果 NAME …...