【C++】string类超详细解析

news2024/11/23 22:51:33

参考文献:C++标准库官网
前言:在C/C++的学习过程当中一定一定要多刷题,牛客网作为国内内容超级丰富的IT题库,尤其是它的C、C++,有从入门到大厂真题,而且大部分的考试题目也是从中抽取,还有很多面经,推荐大家去牛客网进行刷题练习,点击链接:牛客网刷题入口

文章目录

  • 前言
  • 一.string类对象常见的构造函数
  • 二.string类对象的访问及遍历操作
      • 1.operator下标[ ]
      • 2.范围for(C++11支持)
      • 3.迭代器
  • 三.string类对象的容量操作
    • 1.基本:size,capacity,clear,empty
    • 2.reserve函数
    • 3.resize函数
  • 四.string类对象的修改操作
    • 1.operator+=
    • 2.append
    • 3.push_back尾插
    • 4.insert与erase插入与删除
    • 5.assign与replace
    • 6.compare字符串比较
    • 7.swap交换
    • 8.小问题:利用replace将空格都替换成%20涉及find和replace
    • 9.如何获取一个文件名的后缀


前言

在这里插入图片描述

string是C++标准库的一个重要组成部分,主要用于字符串处理。在使用string类是必须包含#include以及using namespace std。

我们从C++标准库的官网中可以知道,basic_string是string类的类模板,那为什么string需要类模板呢?因为在类的实例化中我们可以看到字符串有也有多种类型。这时候有小伙伴会疑问:字符串也有其它类型嘛?答案是:对的,是的。ASCII是最常用的编码,它是最通用的信息交换标准,一个字节8个比特位,最多只能表示256种符号,肯定是不够的,有时就需要多个字节来表示一个符号,比如简体中文常见的编码方式是GBK/GB2312,即使用两个字节表示一个汉字,并且同音字一般挨在一块。由于汉字太多了,包括繁体简体等等,所以才增加了utf-16,utf-32等字符串类型,这样可以扩大文字的数量和种类,这就是为什么string需要类模板的原因,我们在哪种环境或者情况下需要使用字符串,就定义哪种类型的字符串,比如在utf-16的环境下,我们就用U16string。

总结: string类是basic_string模板类的一个实例化结果,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数。这里简单展示一下string的类模板定义。

//动态增长字符数组
template<class T>
class basic_string
{
public:
	//函数
private:
	T* _str;
	size_t _size;
	size_t _capacity;
};
typedef basic_string<char> string;//以char显示实例化basic_string模板类

一.string类对象常见的构造函数

在这里插入图片描述
我们现在来使用一下这些常见的字符串构造函数:

#include<iostream>
#include<string>
using namespace std;
int main()
{
	//default (1)string();构造空字符串
	string s1;
	cout << s1 << endl;//并且支持流插入和流提取

	//copy (2)string(const char* str);
	string s2("诶瑞巴蒂");
	cout << s2 << endl;//输出诶瑞巴蒂
	//追加
	s2 += "嗨起来!";
	cout << s2 << endl;//输出诶瑞巴蒂嗨起来!


	//存在隐式类型转换,编译器会优化
	//先构造string tmp("诶瑞巴蒂");
	//再拷贝给string s3
	string s3 = "诶瑞巴蒂";
	cout << s3 << endl;//输出诶瑞巴蒂

	//string(size_t n,char c)
	string s4(10, '*');
	cout << s4 << endl;//**********

	//副本:拷贝构造函数
	//string(const string& str)
	string s5(s2);
	string s6 = s2;
	cout << s5 << s6 << endl;//输出诶瑞巴蒂嗨起来!

	string s7("hello world", 5);
	cout << s7 << endl;

	string s10(s7, 2);
	cout << s10 << endl;//llo


	string s8(s7, 2, 3);
	cout << s8 << endl;//llo

	string s9(s7, 2, 20);
	cout << s9 << endl;//llo

	string s11(s7, 2, 8);
	cout << s11 << endl;//llo


	return 0;
}

二.string类对象的访问及遍历操作

访问(遍历)string类对象的内容,主要有三种操作方法:
(1)下标[ ] ;
(2)for+下标 或者 范围for(C++11才支持);
(3)迭代器,分为正向迭代器,反向迭代器,const正向,const反向迭代器;

1.operator下标[ ]

在自定义类中,方括号[ ]也是需要我们通过运算符重载自己实现的,很好的是string类内部已经对[]运算符进行了重载并且该重载使用的是引用返回,所以可以通过[ ]+下标对元素进行修改,我们先来简单看一下下标的底层实现。

char& operator[](size_t i)
{
	assert(i < _size);//防止越界访问
	return _str[i];
}

现在再来看看如何用下标遍历string类对象,s1可以直接调用关于string类对象容量操作的函数s1.size(),可以得到s1的长度。注意:下列代码的长度为4,size计算长度时不包括’\0’,类似strlen函数。

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s1("1234");

	for (size_t i = 0; i < s1.size(); ++i)//0-4
	{
		s1[i]++;
	}
	cout << s1 << endl;//2345
	return 0;
}

2.范围for(C++11支持)

若需要通过范围for修改对象的元素,则用于接收元素的变量e的类型必须是引用类型,否则e只是对象元素的拷贝(类似形参),对e的修改不会影响对象的元素。

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s1("1234");

	for (char& e : s1)//如果需要修改,必须加&引用,这里也可用auto& e
	{
		e++;
	}
	cout << s1 << endl;//2345
	return 0;
}

3.迭代器

先来看看C++标准库中有关迭代器的函数接口,这里我们重点学习begin,end,rbegin,rend。
在这里插入图片描述

正向迭代和反向迭代:正向迭代器使用end和begin函数接口,反向迭代器使用rend和rbegin函数接口。

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s1("12345");

	//正向迭代器
	string::iterator it1 = s1.begin();
	while (it1 != s1.end())
	{
		*it1 += 1;//可以把it1看成指针,对其内容进行+1
		++it1;//然后指针++
	}
	it1 = s1.begin();
	while (it1 != s1.end())
	{
		cout << *it1 << " ";//2 3 4 5 6
		++it1;
	}
	cout << endl;

	//反向迭代器
	//string::reverse_iterator rit = s1.rbegin();
	auto rit = s1.rbegin();//觉得类型名太长可以用auto

	while (rit != s1.rend())
	{
		cout << *rit << " ";//6 5 4 3 2
		++rit;
	}
	cout << endl;
	return 

const正向,const反向迭代器:只支持读数据,不能修改容器数据

//const正反向:只支持读,不能修改容器数据
#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s1("12345");

	string::const_iterator it = s1.begin();
	while (it != s1.end())
	{
		//*it += 1;//error
		cout << *it << " ";//1 2 3 4 5
		++it;
	}
	cout << endl;

	auto rit = s1.rbegin();
	while (rit != s1.rend())
	{
		cout << *rit << " ";//5 4 3 2 1
		++rit;
	}
	cout << endl;
	return 0;
}

三.string类对象的容量操作

1.基本:size,capacity,clear,empty

在这里插入图片描述

1.使用size函数或length函数获取当前有效字符的个数(不包括’\0’);
2.使用max_size函数获取字符串可以到达的最大长度,这个值一般非常大,而且不变;
3.使用capacity函数获取当前对象所分配的存储空间大小;
4.使用clear函数删除对象的内容,清除之后对象变为空字符串,注意clear函数不释放空间,只是删除内容;
5.使用empty函数判断对象是否为空,空返回1,不为空返回0;
6.下面着重学习resize和reserve函数的使用。

在这里插入图片描述

2.reserve函数

函数功能:void reserve(size_t n);按要求更改字符串容量。

1.当n大于对象当前的capacity时,将capacity扩大到n,或者大于n(VS编译器下一般会比n大,大概以1.5倍的形式增长,而在LInux下,一般扩容后的空间是原来的两倍。)
2.当n小于对象当前的capacity时,不会缩容,保持原样
3.如果我们自己指定的空间不够,系统会自动扩容。

使用reserve前:
在这里插入图片描述
使用reserve后:如果我们想一次性扩容:
在这里插入图片描述

3.resize函数

在这里插入图片描述

函数功能:设置字符串的长度大小。
如最开始“hello world”的长度是11,当前对象的容量是15。则:
n<11,即相当于删除数据
11<n<=15,即相当于插入数据
n>15相当于扩容+插入数据

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s1("hello world");
	s1.resize(5);
	cout << s1.size() << endl;//5
	cout << s1.length() << endl;//5
	cout << s1.capacity() << endl;//15
	cout << s1 << endl << endl;//hello

	string s2("hello world");
	s2.resize(15, 'x');
	cout << s2.size() << endl;//15
	cout << s2.length() << endl;//15
	cout << s2.capacity() << endl;//15
	cout << s2 << endl << endl;//hello worldxxxx

	string s3("hello world");
	s3.resize(20, 'x');
	cout << s3.size() << endl;//20
	cout << s3.length() << endl;//20
	cout << s3.capacity() << endl;//31
	cout << s3 << endl << endl;//hello worldxxxxxxxxx

	return 0;
}

四.string类对象的修改操作

在这里插入图片描述

1.operator+=

C++标准库的运算符重载函数声明:
在这里插入图片描述

//string类对象的修改
#include<iostream>
#include<string>
int main()
{
	std::string name("John");
	std::string family("Smith");
	name += " K. ";//c-string
	name += family;//string
	name += '!';//char c

	std::cout << name;//John K. Smith!
	return 0;
}

2.append

功能与+=相似

#include <iostream>
#include <string>
int main()
{
	std::string str;
	std::string str2 = "Writing ";
	std::string str3 = "print 10 and then 5 more";

	// used in the same order as described above:
	str.append(str2);                       // "Writing "
	str.append(str3, 6, 3);                   // "10 "
	str.append("dots are cool", 5);          // "dots "
	str.append("here: ");                   // "here: "
	str.append(10u, '.');                    // ".........."
	str.append(str3.begin() + 8, str3.end());  // " and then 5 more"

	std::cout << str << '\n';
	//Writing 10 dots here: .......... and then 5 more
	return 0;
}

3.push_back尾插

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s;
	s.push_back('C');
	s.push_back('S');
	s.push_back('D');
	s.push_back('N');
	cout << s << endl; //CSDN
	return 0;
}

4.insert与erase插入与删除

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s("C"); //C

	//insert(pos, str)在pos位置插入字符串str
	s.insert(1, "S"); //CS

	//insert(pos, string)在pos位置插入string对象
	string t("D");
	s.insert(2, t); //CSD

	//insert(pos, char)在pos位置插入字符char
	s.insert(s.end(), 'N'); //CSDN
	
	cout << s << endl; //CSDN
	return 0;
}

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s("I like C++!!!");

	//erase(pos, n)删除pos位置开始的n个字符
	s.erase(8, 5); //I like C

	//erase(pos)删除pos位置的字符
	s.erase(s.end()-1); //I like

	//erase(pos1, pos2)删除[pos1pos2)上所有字符
	s.erase(s.begin() + 1, s.end()); //I

	cout << s << endl; //I
	return 0;
}

5.assign与replace

#include <iostream>
#include <string>
using namespace std;
int main()
{
	//assign
	string s1("hello world hello world");
	string s2("hello world hello world");

	s1.assign("hello zxn", 5);//赋值string的前5个字符
	cout << s1 << endl;//hello

	//从第6个字符开始,将5个字符都替换成"zxn"
	s2.replace(6, 5, "zxn");
	cout << s2 << endl; //hello zxn hello world

	return 0;
}

6.compare字符串比较

注意比较的是ASCII码值。
1.两个字符串相等,则返回0;
2.比较字符串中第一个不匹配的字符值较小,或者所有比较字符都匹配,但比较字符串较短,则返回小于0的值。
3.比较字符串中第一个不匹配的字符值较大,或者所有比较字符都匹配,但比较字符串较长,则返回大于0的值。

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s1("hello world");
	string s2("hello CSDN");

	//"hello world"和"hello CSDN"比较
	cout << s1.compare(s2) << endl; //1

	//"ell"和"hello CSDN"比较
	cout << s1.compare(1, 3, s2) << endl; //-1

	//"hello"和"hello"比较
	cout << s1.compare(0, 4, s2, 0, 4) << endl; //0

	return 0;
}

7.swap交换

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s1("hello");
	string s2("CSDN");
	
	//使用string类的成员函数swap交换s1和s2
	s1.swap(s2);
	cout << s1 << endl; //CSDN
	cout << s2 << endl; //hello

	//使用非成员函数swap交换s1和s2
	swap(s1, s2);
	cout << s1 << endl; //hello
	cout << s2 << endl; //CSDN
	return 0;
}

8.小问题:利用replace将空格都替换成%20涉及find和replace

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s1("hello world hello world");

	//1.不建议这种算法,在数据挪动方面消耗了太多时间
	size_t pos = s1.find(' ');
	while (pos != string::npos)//string::npos
	{
		s1.replace(pos, 1, "20%");
		pos = s1.find(' ', pos + 3);//pos当前指向2,跳过3个字符,继续找
	}
	cout << s1 << endl;

	//2.以空间换时间
	string ret;
	ret.reserve(s1.size());
	//范围for
	for (auto ch : s1)
	{
		//不为' '就插入,是' '就插入%20
		if (ch != ' ')
		{
			ret += ch;
		}
		else
		{
			ret += "%20";
		}
	}
	cout << ret << endl;

	return 0;
}

9.如何获取一个文件名的后缀

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string file;
	cin >> file;

	//想要直到文件名的后缀,要先找到'.'
	//size_t pos = file.find('.');//正向找
	size_t pos = file.rfind('.');//反向找

	if (pos != string::npos)//pos在安全范围内
	{
		string suffix1 = file.substr(pos);
		cout << suffix1 << endl;
	}
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/25764.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智慧职教解决方案-最新全套文件

智慧职教解决方案-最新全套文件一、建设背景二、建设思路三、建设方案四、获取 - 智慧职教全套最新解决方案合集一、建设背景 职业教育目前存在的问题&#xff1a; 发展理念相对落后国际化程度不高基本制度不健全层次结构不合理基础能力相对薄弱社会吸引力不强行业企业参与不…

20221121将行车记录仪记录的MJPEG格式的AVI片段合并的MKV转换为MP4

20221121将行车记录仪记录的MJPEG格式的AVI片段合并的MKV转换为MP4 2022/11/21 21:51 &#xff08;一&#xff09; 缘起&#xff0c;用行车记录仪录制的爬拉胡线&#xff08;惠州大南山&#xff09;的AVI视频&#xff0c;一个片段5分钟。 使用mkvtoolnix-gui将AVI合并成为MKV视…

垂直定位系统实验平台

系统概述 本系统由控制系统和被控对象两部分组成&#xff0c;可根据课程需要进行双轴机构的轴数增减和循序渐进的运动控制实训。 控制系统部分由水平轴执行机构、人机界面机构、电源机构、驱动元件与控制器等组成。控制方式&#xff1a;人机界面模拟控制。 控制对象部分由水…

如何制作传统节日网站(纯HTML代码)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

clean-label backdoor attacks 论文笔记

#论文笔记# 1. 论文信息 论文名称Clean-Label Backdoor Attacks作者Alexander Turner(MIT)会议/出版社ICLR 2019pdf本地pdf 在线pdf代码trojanzoo-clean-label**** Label-Consistent其他这篇文章和 Label-Consistent Backdoor Attacks 基本相同 简介&#xff1a;这篇文章是最…

Java内部类解析

作者&#xff1a;~小明学编程 文章专栏&#xff1a;JavaSE基础 格言&#xff1a;目之所及皆为回忆&#xff0c;心之所想皆为过往 目录 什么是内部类 静态内部类 静态内部类访问外部类的规则 外部类访问静态内部类的规则 实例化静态内部类 非静态内部类 内部类访问外部类…

【网安神器篇】——hydra爆破工具

作者名&#xff1a;Demo不是emo 主页面链接&#xff1a;主页传送门创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座右…

2.16 这些都是我常用的修图工具【玩赚小红书】

一、美图秀秀 P图、拼图、贴纸文字、抠图、视频剪辑等该有的功能都有&#xff0c;有很多现成的高颜值模板可以直接套用。 软件官网&#xff1a;https://pc.meitu.com/ 支持&#xff1a;windows&#xff0c;linux&#xff0c;苹果&#xff0c;安卓手机&#xff0c;苹果手机 ​…

是js高级啊~

JavaScript高级 1.函数 1.1. this绑定规则优先级 隐式绑定的this 大于 默认的this显式绑定的this 大于 隐式绑定的thisnew绑定this 大于 隐式绑定的thisnew绑定的this 大于 bind绑定的优先级bind绑定的this 大于 apply、call绑定的this 备注&#xff1a; new关键字不可以和…

使用idea工具实现新建java项目并连接mysql数据库

1.新建java项目 如果刚打开idea工具&#xff0c;则选择Create new Project。 在里新建使用File > New > Project 新建package包和class文件 右键src > new > package后输入包名 在刚建的包下右键选择 new > Java class后并输入类名 链接mysql Java 连…

win10系统下使用opencv-dnn部署yolov5模型

文章目录前言一、环境1、硬件2、软件二、YOLO模型三、新建Qt项目1、pro文件2、main函数四、YOLO 类封装1、yolo.h2、yolo.cpp3、classes.txt五、效果前言 最近工作中需要用yolov5训练模型&#xff0c;然后在win10系统下&#xff0c;完成推理部署。本篇主要介绍使用opencv-dnn模…

7判断环的入口结点8输出倒数第k个

7.判断环的入口结点 第一次第二次看感觉都无从下手&#xff0c;还是看了题解&#xff0c;主要思路是&#xff1a; 假设前面无环的结点有a个&#xff0c;环有b个结点&#xff0c;则设置快慢结点之后&#xff0c;fast结点一次走两步&#xff0c;slow结点一次走一步。则相遇时fast…

《第一行代码》核心知识点:活动(Activity)的儿子叫碎片(Fragment)

《第一行代码》核心知识点&#xff1a;活动Activity的儿子叫碎片Fragment前言四、活动(Activity)的儿子叫碎片&#xff08;Fragment&#xff09;4.1 碎片是神马&#xff1f;4.2 碎片的基本使用4.3 向容器中动态添加碎片4.4 活动与碎片之间通信方法4.5 碎片的生命周期4.6 使用限…

重载单目运算符以及重载运算符的注意事项

一、单目运算符的重载 单目运算符可分为两种&#xff1a; 1&#xff09;可以放在前面&#xff0c;也可以放在后面的单目&#xff0c;如&#xff1a; -- 2&#xff09;只能放在前面的运算符&#xff1a;&#xff01; &#xff08;正号&#xff09; -&#xff08;负号&#x…

Python函数详解(四)——Python函数参数使用注意事项

今天继续给大家介绍Python相关知识&#xff0c;本文主要内容是Python函数参数使用注意事项。 在上文Python函数详解&#xff08;三&#xff09;——函数的参数传递进阶中&#xff0c;我们学习了函数参数的进阶内容。今天&#xff0c;我们来学习函数的参数使用注意事项。 一、P…

【微信小程序】使用 Cryptiojs 解密微信绑定手机号码

很抱歉断更了一段时间&#xff0c;因为最近在做一个项目比较忙&#xff0c;正好项目中小程序板块需要解密手机号码来提交给接口&#xff0c;小程序中虽然提供了获取手机号按钮点击事件&#xff1a;bindgetphonenumber&#xff0c;但是该事件的处理函数中只能获取到加密过的手机…

关于如何调节Mahony AHRS算法的参数

文章目录一、Mahony算法的控制系统特征多项式二、Kp, Ki参数调节方法三、其他自适应调参法我在之前的博客AHRS互补滤波&#xff08;Mahony&#xff09;算法及开源代码中曾提及Mahony算法的难点在于如何调节PI参数。 最近看到参考文献[1]&#xff0c;提出了基于无阻尼自由频率设…

C# 将一种类型的数组转换为另一种类型的数组

将一种类型的数组转换为另一种类型的数组 public static TOutput[] ConvertAll<TInput, TOutput>(TInput[] array, Converter<TInput, TOutput> converter); 参数: array: 要转换为目标类型的从零开始的一维 System.Array。 converter: …

【数据结构与算法】杨辉三角,相同字符的截取以及扑克牌

✨个人主页&#xff1a;bit me ✨当前专栏&#xff1a;数据结构 ✨每日一语&#xff1a;不要等到了你的人生垂暮&#xff0c;才想起俯拾朝花&#xff0c;且行且珍惜。 ArrayList题训&#x1f335; 一. 杨辉三角&#x1f334;二.相同字符的截取&#x1f332;三.扑克牌&#x1f…

【SpringCloud】02 搭建springcloud微服务项目,服务治理组件nacos,负载均衡ribbon,远程调用Openfeign

文章目录搭建springcloud微服务项目1. 微服务父工程2. 创建子模块-shop-common3. 创建子模块--shop-product4. 创建子模块--shop-order服务治理组件1. 如何使用nacos2. 微服务客户端连接到nacos注册中心3. 消费端如何通过nacos调用提供者负载均衡1. 自己控制负载均衡2. ribbon完…