【C++】——string类的使用

news2024/11/22 21:27:09

目录

一.为什么学习string类?

1.1 C语言的字符串

二. 标准库中的string类

2.1 string类(了解)

2.2 string类成员函数

● string类对象的常见构造

● string类析构函数

● 赋值重载

2.3 string的迭代器

<1>正向迭代器 Iterator

<2> 反向迭代器 reverse_iterator   

<3> 迭代器访问const类型容器

2.4 string类的容量操作

2.5 string类的访问修改

<1>元素访问

<2>元素修改

<3>子字符串

2.6 string类的查找

2.7 string类的非成员函数

● 运算符重载+

● 比较运算符重载

● getline(将线从流转换为字符串)


一.为什么学习string类?

1.1 C语言的字符串

C语言中,字符串是以 '\0' 结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合面向对象编程(OOP)的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、 快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。

string类相比C语言的字符串更安全、更方便、更高效的使用,是面向对象编程的思想体现

二. 标准库中的string类

2.1 string类(了解)

string类的文档介绍

在使用string类时,必须包含头文件#include<string>以及using namespace std;

2.2 string类成员函数

● string类对象的常见构造

<1>string(); 无参构造,空的 string 只有  "\0"

<2>string (const string& str); 拷贝构造函数

<3>string (const string& str, size_t pos, size_t len = npos);

(其他负数也可以起到一样的效果)

拷贝构造,在指定下标 pos 位置拷贝 len 个字符 默认值为 npos 类型为无符号整数类型,因此 npos 为无符号整数最大值,库规定 len 大于字符串长度,则拷贝整个 str 到结束

<4>string (const char* s); 将字符串拷贝给 string 类

<5>string (const char* s, size_t n); 将字符串的前 n 个拷贝给 string 类

<6>string (size_t n, char c); 将 n 个字符 c 拷贝给 string 类

int main()
{
	string s1;//无参构造 只有\0
	string s11{};//同上  string s1(); 编译器可能认为声明
	string s2("hello world");//将括号内字符串拷贝给s2
	string s3(s2, 0, 2);//将s2从第0个位置拷贝2个元素给s3
	string s4(s2);//拷贝构造 将s2类拷贝给s4
	string s5(s2, 5);//拷贝构造 将s2类的前5个字符拷贝给s5
	string s6(8, 'a');//拷贝n个字符a给s6

	cout << s1 << endl;
	cout << s11 << endl;
	cout << s2 << endl;
	cout << s3 << endl;
	cout << s4 << endl;
	cout << s5 << endl;
	cout << s6 << endl;

	return 0;
}

● string类析构函数

当字符串生命周期结束,会自动调用析构函数。

● 赋值重载

2.3 string的迭代器

<1>正向迭代器 Iterator

提供了一种通用的(所用)访问容器的方式

string s1("hello");
// it 作用类似指针
string::iterator it = s1.begin();
while (it != s1.end())//end 返回最后一个字符下一个位置 左闭右开
{
	*it += 2;//可以修改内容
	cout << *it << ' ';
	++it;
}
cout << endl;

● begin  返回 指向 string 第一个字符

● end 返回 指向 string 最后一个有效字符下一个位置 \0

<2> 反向迭代器 reverse_iterator   
string s1("hello");
// it 作用类似指针
string::reverse_iterator it = s1.rbegin();//反向指向string的最后一个有效字符 视为开始端
auto end = s1.rend();//指向 string的第一个有效字符前一个位置 视为结尾端

while (it != s1.rend())
{
	cout << *it << ' ';
	++it;
}
cout << endl;

● rbegin

● end

<3> 迭代器访问const类型容器

普通的迭代器是可读可写的,const迭代器访问const修饰的容器只能读不能写(指向的内容)

Tip:const迭代迭代器不是指const 修饰迭代器,因为迭代器本身可以修改,而它指向的内容不能修改!

string s1("hello");
// it 作用类似指针
string::const_iterator it = s1.cbegin();  //也可以写成 begin 相当于权限缩小
while (it != s1.end())
{
	/**it += 2;*/
	cout << *it << ' ';
	++it;
}
cout << endl;

三种访问方式:

string s1("hello world");
cout << s1 << endl;
for (int i = 0; i < s1.size(); i++)
{
	cout << s1[i] << ' ';
}
cout << endl;

string::iterator it = s1.begin();
auto it2 = s1.begin();
while (it2 != s1.end())
{
	cout << *it2 << ' ';
	it2++;
}
cout << endl;

//自动推导类型 自动赋值 自动迭代结束 底层迭代器
//本质拷贝赋值 不能修改原数据 将*it值 赋值给 c
for (auto c : s1)
{
	cout << c << ' ';
}
cout << endl;

//引用 可以修改
for (auto& c : s1)
{
	cout << c << ' ';
}
cout << endl;

2.4 string类的容量操作

std::string 里内部结构可能优化了,比如 char[] _buff 在这种情况下,短字符串可能直接存储在 std::string 对象的内存空间内,而不是在堆上分配。但是,当字符串增长超出了这个内部空间时,就会触发堆上的内存分配和扩容。

连续尾插测试扩容机制:

int main()
{
	string s;
	size_t sz = s.capacity();
	cout << "capacity changed:" << sz << '\n';

	cout << "making a 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';
		}
	}
	return 0;
}

可以看到 由于 _buff 的存在 初始容量为15个字节,再继续尾插之后该编译器出现了 1.5倍的扩容机制以减少将来可能的内存重新分配次数,提高性能。

容量只显示有效字符的个数,内存会比容量多一个空间(存储‘ \0 ’)

● 总结:

<1> size() 与 length() 方法底层实现原理完全相同,引入size() 的原因是为了与其他容器的接

口保持一致, 一般情况下基本都是用size() 

<2> capacity() 返回有效数据空间大小,不包含 \0 实际需要多开一个空间

<3> clear() 将string中有效字符清空,不改变底层空间大小。

<4>  resize(size_t n) 与 resize(size_t n, char c) 都是将字符串中有效字符个数改变到n个 ,不
同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char
c) 用字符c来填充多出的元素空间 。

注意: resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。

<5>reserve(size_t res_arg=0) 为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。

vs下,有效元素个数小于预留空间大小时,不缩容。

g++下,有效元素个数小于预留空间大小时,缩容。

int main()
{
	string s2("hello xxxxxxxxxxxxxxxxxx");
	cout << s2.size() << endl;
	cout << s2.capacity() << endl << endl;

	//给一个小于 size的值
	s2.reserve(20); //结果不变
	cout << s2.size() << endl;
	cout << s2.capacity() << endl << endl;

	//给一个 size与 capacit 中间值
	s2.reserve(28); //结果不变
	cout << s2.size() << endl;
	cout << s2.capacity() << endl << endl;

	//给一个 大于 capacit 值
	s2.reserve(40); //扩容
	cout << s2.size() << endl;
	cout << s2.capacity() << endl << endl;

	s2.clear();//清楚数据 不清容量
	cout << s2.size() << endl;
	cout << s2.capacity() << endl << endl;
	return 0;
}

2.5 string类的访问修改

<1>元素访问

● 运算符重载[ ]  返回pos位置字符的引用

模拟实现:

char& operator[](size_t pos)
{
	assert( pos>=0 &&pos < _size);
	return _str[pos];
}

const char& operator[](size_t pos)const
{
	assert(pos >= 0 && pos < _size);
	return _str[pos];
}

● at 获取字符串中的字符

at 函数自动检查 pos 是否是字符串中字符的有效位置(即 pos 是否小于字符串长度),如果不是,则抛出 out_of_range 异常。

<2>元素修改

● push_back   尾部插入一个字符c

● append   在字符串后追加字符串str

赋值重载+=   在字符串后追加字符串str 

int main()
{
	string s2("world");
	string s("hello worold");
	s.push_back(' ');//单个字符
	s.push_back('x');
	s.append(" ");
	s.append(s2);//字符串
	cout << s << endl;
	s += ' ';
	s += "ccccccc";
	cout << s << endl;

	return 0;
}

注意:

1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c' 三种的实现方式差不多,一般情况下string类的 += 操作用的比较多,+= 操作不仅可以连接单个字符,还可以连接字符串。

2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

● insert(插入) 在pos或p位置之前插入字符串

● erase(删除)pos位置删除字符串

 ● replace(替换)

int main()
{

	string s("hello worold");
	//模拟头插 需要移动数据 效率相对低
	s.insert(0, "hello xc ");
	cout << s << endl;
	//模拟头删 需要移动数据
	s.erase(0, 1);
	cout << s << endl;
	s.erase(s.begin());
	cout << s << endl;
	//模拟尾删
	s.erase(--s.end());
	cout << s << endl;
	s.erase(s.size() - 1, 1);
	cout << s << endl;
	//将空格替换
	string ss("hello world");
	cout << ss << endl;
	ss.replace(5, 1, "%%");
	cout << ss << endl;
	cout << endl;
	return 0;
}

<3>子字符串

c_str(兼容C)返回一个指向字符数组的指针 数组内容即 string 对象

// string 不能直接与 文件操作对接
string file;
cin >> file;
FILE* fout = fopen(file.c_str(),"r");
char ch = fgetc(fout);
while (ch != EOF)
{
	cout << ch;
	ch= fgetc(fout);
}
fclose(fout);

● data(类似于c_str)

substr(子串)在str中从pos位置开始,截取n个字符,然后将其返回

2.6 string类的查找

find(正向) 在字符串里查找字符数据 pos位置开始 n个数据

//将字符串的空格替换成百分号

//法一 移动数据频繁 效率低
string sss("hello world hello bit");
cout << sss << endl;
size_t pos = sss.find(' ');
while (pos != string::npos)
{
	sss.replace(pos, 1, "%%");
	pos = sss.find(' ',pos+2);
}
cout << sss << endl<<endl;
//法二 构造新串 空间换时间
string tmp;
tmp.reserve(sss.size());
for (auto ch : sss)
{
	if (ch == ' ')
		tmp += "%%";
	else
		tmp += ch;
}
cout << tmp << endl;
sss.swap(tmp);// 交换 字符指针
cout << sss << endl << endl;

● rfind(倒着找) 与find 参数一致意义 倒着查找

//找出文件后缀
string s2("test.cpp.zip");
size_t pos2 = s2.rfind('.');
string suffix2 = s2.substr(pos2);
cout << suffix2 << endl;

● find_first_of  在字符串中搜索与其参数中指定的任何字符匹配的第一个字符。

int main()
{
	string s3("hello world hello xc");
	cout << s3 << endl;
	size_t pos3 = s3.find_first_of("abcd");// find_first_of 给定一个需要查找的字符串,一个一个查找,返回索引 
	while (pos3 != string::npos)			
	{										
		s3[pos3] = '*';
		pos3 = s3.find_first_of("abcd", pos3 + 1);
	}
	cout << s3 << endl;
	return 0;
}

● find_last_of(倒着找)倒序查找


void SplitFilename(const std::string& str)
{
	std::cout << "Splitting: " << str << '\n';
	std::size_t found = str.find_last_of("/\\");
	std::cout << " path: " << str.substr(0, found) << '\n';
	std::cout << " file: " << str.substr(found + 1) << '\n';
}

//文件路径分离
string str1("/usr/bin/man");
string str2("c:\\windows\\winhelp.exe");
SplitFilename(str1);
SplitFilename(str2);

● find_first_not_of    查找字符串中与参数中指定的任何字符都不匹配的第一个字符

● find_last_not_of    倒叙查找字符串中与参数中指定的任何字符都不匹配的第一个字符

2.7 string类的非成员函数

● 运算符重载+

返回一个新构造的字符串对象,其值是lhs中字符的连接,后跟rhs中的字符。

int main()
{
	string s1("hello");
	string s2 = s1 + " wrold";
	string s3 = s1 + s2;
	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;
	return 0;
}

● 比较运算符重载

在字符串对象lhs和rhs之间执行适当的比较操作

● getline(将线从流转换为字符串)

从is中提取字符并将其存储到str中,直到找到分隔符delim

找最后一个单词长度:

#include <iostream>
using namespace std;
 
int main() {
   string str;
   //cin >> str;// cin 和 scanf 将空格和换行默认成分割
   getline(cin,str);// 无定界默认遇到换行才停止输入
   size_t pos = str.rfind(' ');
   cout << str.size() - (pos + 1) << endl;
}

可以自定义分隔符

getline(cin,str,"*");//流提取,直到遇到*才停止

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

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

相关文章

接口测试工具:Postman详解

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、前言 在前后端分离开发时&#xff0c;后端工作人员完成系统接口开发后&#xff0c;需要与前端人员对接&#xff0c;测试调试接口&#xff0c;验证接口的正确性…

面试笔试 场景题(部分总结)

文章目录 题目--找出一堆随机数中的前 k 大数字PriorityQueue 类PriorityQueue 常用方法 题目--数组中的第 K 个最大元素 题目–找出一堆随机数中的前 k 大数字 找出一堆随机数中的前 k 大数字&#xff0c;找出一堆随机数中的前 k 小数字。都一样 方法&#xff1a;快速排序。…

【springsecurity】使用PasswordEncoder加密用户密码

目录 1. 导入依赖2. 配置 PasswordEncoder3. 使用 PasswordEncoder 加密用户密码4. 使用 PasswordEncoder 验证用户密码 1. 导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifac…

docker部署NginxProxyManager

1.效果展示 有啥用&#xff1f;&#xff0c; 能把域名代理到 117.xx.xx.xx:3000 服务器上 而不用写一堆的location / { proxy… } 2.步骤 2.1往期docker安装 docker和docker-compose安装脚本-CSDN博客 2.2创建目录 mkdir ~/npm #创建一个目录用来安装此服务 cd ~/npm #…

兴业周报|“一套房子不留”的马斯克 在北京买房了?

稀有房源&#xff1a;标的物位于北京五环内&#xff0c;真正的城市别墅。中间建筑三区“全网无二手”&#xff0c;二手房市场无在售房源。 高性价比&#xff1a;标的物市场价2800万&#xff0c;起拍价1124万&#xff0c;四折上拍&#xff0c;四室两厅南北通透&#xff0c;建面…

基于SpringBoot的口腔管理平台

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot框架 工具&#xff1a;IDEA/Eclipse、Navicat、Maven 系统展示 首页 会员管理 病例就诊…

RuoYi-Vue若依框架-系统监控内定时任务的使用

定时任务 之前定时任务是在后端代码内使用cron表达式对对应的接口方法按照表达式执行&#xff0c;现在可以在框架内自行添加任务&#xff0c;包括自定义任务名&#xff0c;在调用方法内写对应实体类.接口方法名&#xff0c;cron表达式&#xff0c;添加好后即可执行 调用方法…

业余考什么证书比较实用?

在业余时间里&#xff0c;获得一些有用的证书不仅能提升你的专业素养&#xff0c;还能增强你在职场上的竞争力。 特别是职业技能证书和行业认证证书&#xff0c;这两者受到了广大职场人士的高度关注。 一、业余时间考取的实用证书 行业认证证书主要针对特定行业或职业&#…

16 训练自己语言模型

在很多场景下下&#xff0c;可能微调模型并不能带来一个较好的效果。因为特定领域场景下&#xff0c;通用话模型过于通用&#xff0c;出现多而不精。样样通样样松&#xff1b;本章主要介绍如何在特定的数据上对模型进行预训练&#xff1b; 训练自己的语言模型&#xff08;从头开…

AT3340-6T杭州中科微BDS定位授时板卡应用领域

AT3340-6T是一款高性能多系统卫星定位安全授时板卡&#xff0c;具有高灵敏度、高精度、抗干扰、低功耗等优势&#xff0c;可以满足电力授时、通信授时和金融授时等领域的应用。 电文完好性检测与告警 AT3340-6T 板卡支持电力授时标准要求的 UTC 信息异常告警&#xff0c;和卫…

基于Spring Boot的高校选课平台的设计与实现81917

目 录 1 绪论 1.1研究背景与意义 1.2研究内容 1.3论文结构与章节安排 2 高校选课平台系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据流程 3.3.2 业务流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 高校选课…

【OpenCV-图像梯度】Scharr算子和laplacian算子

1 图像梯度-Sobel算子 import cv2 # 导入 OpenCV 库# 读取灰度图像 img cv2.imread(./img/pie.png, cv2.IMREAD_GRAYSCALE) # 使用 imread 函数读取图像文件&#xff0c;路径为 ./img/pie.png # cv2.IMREAD_GRAYSCALE 表示以灰度模式读取图像# 显示图像 cv2.imshow("im…

Muduo库介绍及使用

文章目录 MuduoMuduo库是什么Moduo 库的原理Muduo 库常见接口TcpServer类EventLoop类TcpConnection类TcpClient类Buffer类 Muduo库实现一个简单英译汉服务器和客⼾端 Muduo Muduo库是什么 Muduo由陈硕⼤佬开发&#xff0c;是⼀个基于⾮阻塞IO和事件驱动的C⾼并发TCP⽹络编程库…

SpringSecurity原理解析(三):请求流转过程

1、当web系统启动的时候SpringSecurity做了哪些事情&#xff1f; 当web系统启动的时候会加载WEB-INF下的web.xml文件&#xff0c;在web.xml主要配置了下边几块的 内容&#xff0c;分别是&#xff1a; 1&#xff09;加载classpath路径下的配置文件&#xff08;包括SpringSecurit…

开关电源自动化测试有哪些流程和步骤?-天宇微纳

开关电源自动化测试的总体流程包括测试前的准备、测试过程中的具体步骤和测试后的数据处理三大部分。 以纳米软件ATECLOUD平台测试为例&#xff1a; ‌测试前的准备‌ ‌连接设备和仪器‌&#xff1a;通过LAN通讯总线、测试夹具以及其它线缆将需要测试的电源模块连接到纳米服…

【C语言】指针深入讲解(下)

目录 前言回调函数回调函数的概念回调函数的使用 qsort函数的使用和模拟实现qsort函数的介绍qsort函数的使用qsort函数模拟实现 前言 今天我们来学习指针最后一个知识点回调函数&#xff0c;这个知识点也很重要&#xff0c;希望大家能坚持学习下去。 没学习之前指针知识内容的…

k8s的配置管理

一、配置管理分为两种&#xff1a; 1. 加密配置&#xff1a;用来保存密码和token密钥对以及其它敏感的k8s资源。 2.应用配置&#xff1a;我们需要定制化的给应用进行配置&#xff0c;我们需要把定制好的配置文件同步到pod当中的容器。 二、加密配置 1.secret三种类型&#xf…

WPS取消首字母自动大写

WPS Office&#xff08;12.1.0.17827&#xff09; ① 点击文件&#xff0c;在文件中找到选项 ② 选择编辑&#xff0c;取消勾选

三国地理揭秘:为何北伐之路如此艰难,为何诸葛亮无法攻克陇右小城?

俗话说:天时不如地利&#xff0c;不是随便说说&#xff0c;诸葛亮六出祁山&#xff0c;连关中陇右的几座小城都攻不下来&#xff0c;行军山高路险&#xff0c;无法携带和建造攻城器械&#xff0c;是最难的&#xff0c;所以在汉中&#xff0c;无论从哪一方进攻&#xff0c;防守方…

计算机为啥选中二进制?

坊间传闻&#xff0c;当年&#xff0c;彷徨少年computer有幸读到东方奇书《道德经》中一段&#xff1a;“道生一&#xff0c;一生二&#xff0c;二生三&#xff0c;三生万物。”忽然灵光乍现&#xff0c;做五体投地状。“啊门、主啊&#xff0c;我get到了&#xff0c;狗屁二生三…