C++入门7——string类详解

目录

1.什么是string类?

2.string类对象的常见构造

2.1 string();

2.2 string (const char* s);

2.3 string (const string& str);

2.4 string (const string& str, size_t pos, size_t len = npos);

2.5 string (const char* s, size_t n);

2.7 验证:

3.string类对象的遍历及访问 

3.1 下标+ [] 访问

3.2 iterator迭代器访问

3.3 at访问 

4. string类对象的容量操作

4.1 size与length

4.2 capacity

4.3 reserve

4.4 resize

4.5 clear

4.6 empty

5.string类对象的修改操作

5.1 增

1. append

2. +=

3.insert 

5.2 删

1. pop_back

2. erase

5.3 查 

1.substr

2.find

5.4 改

1. replace

2. swap 

5.5 查改结合完成替换操作


1.什么是string类?

在官网中,string类有这样的介绍:

Strings are objects that represent sequences of characters.

即:string类表示的对象是字符串类。

1. string是表示字符串的字符串类

2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。

3. string在底层实际是:basic_string模板类的别名,typedef basic_string string;

4. 不能操作多字节或者变长字符的序列。

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

2.string类对象的常见构造

2.1 string();

在官网中,对string();的解释为:

Constructs an empty string, with a length of zero characters.

即:构造一个空字符串,长度为零字符。

int main()
{
	//string();定义一个空的string类
	string s1;
	return 0;
}

2.2 string (const char* s);

在官网中,对于string (const char* s);的解释为:

Copies the null-terminated character sequence (C-string) pointed by s.

即:复制s指向的以空字符结尾的字符串。

int main()
{
	//string (const char* s);复制s指向的以空字符结尾的字符串
	string s2("hello world!");
	return 0;
}

2.3 string (const string& str);

在官网中,对string (const string& str);的解释为:

Constructs a copy of str.

即可以理解为拷贝构造。

int main()
{
    //拷贝构造s2
	string s3(s2);
	string s4 = s2;
	return 0;
}

2.4 string (const string& str, size_t pos, size_t len = npos);

在官网中,对string (const string& str, size_t pos, size_t len = npos);的解释为:

Copies the portion of str that begins at the character position pos and spans len characters.

即:拷贝str从pos位置到len个字符的一部分。

int main()
{
   	//string (const string& str, size_t pos, size_t len = npos);
	//拷贝str从pos位置到len个字符的一部分。
	string s5(s2, 1, 7);
	return 0;
}

可在官网里括号里还有一句话:  (or until the end of str, if either str is too short or if len is string::npos).

这句话可以理解为:如果str太短或者len太长,那就只拷贝到str的结尾。

2.5 string (const char* s, size_t n);

在官网中,对string (const char* s, size_t n);的解释为:

Copies the first n characters from the array of characters pointed by s.

即:拷贝s指向的字符数组的前n个字符。

int main()
{
   //string (const char* s, size_t n);
	//拷贝s指向的字符数组的前n个字符
	string s6("hello world!", 5);
	return 0;
}

2.6 string (size_t n, char c);

在官网中,对string (size_t n, char c);的解释为:

Fills the string with n consecutive copies of character c.

即:用n个C语言字符填充。

int main()
{
    //string (size_t n, char c);
	//用n个C语言字符填充
	string s7(10, 'l');
}

2.7 验证:

<<与>>已实现重载,所以可以直接用。

故验证代码如下:

#include<iostream>
#include<string>
using namespace std;
int main()
{
	//string();定义一个空的string类
	string s1;

	//string (const char* s);复制s指向的以空字符结尾的字符串
	string s2("hello world!");

	//string (const string& str);拷贝构造s2
	string s3(s2);
	string s3_1 = s2;

	//string (const string& str, size_t pos, size_t len = npos);
	//拷贝str从pos位置到len个字符的一部分。
	string s4(s2, 1, 7);//拷贝s2从下标1位置开始到7个字符的一部分

	//string (const char* s, size_t n);
	//拷贝s指向的字符数组的前n个字符
	string s5("hello world!", 5);//拷贝"hello world!"的前5个字符

	//string (size_t n, char c);
	//用n个C语言字符填充
	string s6(10, 'l');//拷贝10个‘l’

	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;
	cout << s3_1 << endl;
	cout << s4 << endl;
	cout << s5 << endl;
	cout << s6 << endl;
	return 0;
}

结果如图:

3.string类对象的遍历及访问 

3.1 下标+ [] 访问

①计算string类的长度或大小

 (size与length意义相同,建议用size)用法如下:

int main()
{
	string s1("hello world!");
	cout << s1.size() << endl;
	cout << s1.length() << endl;
	return 0;
}

(由结果得知,size与length不包含'\0') 

② 知道了string的长度,我们就可以这样遍历string:

int main()
{
	string s1("hello world!");
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << endl;//重载了s1.operator[](i)
	}
	return 0;
}

 ③实现逆置string

方法一:手搓交换

int main()
{
	string s1("hello world!");
	cout << "逆置前s1="<< s1 << endl;
	size_t begin = 0;//左边界
	size_t end = s1.size() - 1;//右边界
	while (begin < end)
	{
		char tmp = s1[begin];
		s1[begin] = s1[end];
		s1[end] = tmp;

		++begin;
		--end;
	}
	cout << "逆置后s1=" << s1 << endl;
	return 0;
}

方法二:使用swap

int main()
{
	string s1("hello world!");
	cout << "逆置前s1="<< s1 << endl;
	while (begin < end)
	{
		swap(s1[begin], s1[end]);

		++begin;
		--end;
	}
	cout << "逆置后s1=" << s1 << endl;
	return 0;
}

3.2 iterator迭代器访问

int main()
{
	string s1("hello world!");
    string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << endl;
		++it;
	}
	return 0;
}

iterator的用法与指针类似:

二者的区别:

 下标+[]只适用于部分容器,底层物理有一定连续,如链式结构、树形、哈希结构,就只能用迭代器。迭代器才是容器访问的主流形式。

3.3 at访问 

at访问与下标+[]访问的功能相同,区别主要体现在越界访问时的报错形式:

如①下标+[]的越界访问:

int main()
{
	string s1("hello world!");
	cout << s1[20] << endl;
	return 0;
}

 ②at的越界访问:

int main()
{
	string s1("hello world!");
	cout << s1.at(20) << endl;
	return 0;
}

(即下标+[]是暴力地终止,at是温柔地终止) 

4. string类对象的容量操作

4.1 size与length

size与length返回字符串有效字符长度

前面已经说过,size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。

代码演示:

int main()
{
	string s1("hello world!");
	cout << s1.size() << endl;
	return 0;
}

4.2 capacity

capacity返回空间总大小

代码如下:

int main()
{
	string s1("hello world!");
	cout << s1.capacity() << endl;
	return 0;
}

设计程序检测string的扩容机制:

int main()
{
	string s1("hello world!");
	//检测string的扩容机制
	size_t old = s1.capacity();    //令old=原本的容量大小
	cout << old << endl;        
	for (size_t i = 0; i < 500; i++)
	{
		s1.push_back('l');         //尾插l的过程中string的容量大小一定会发生变化
		if (old != s1.capacity())  //当发生扩容时,打印新的string容量大小
		{
			cout << s1.capacity() << endl;
			old = s1.capacity();
		}
	}
	return 0;
}

4.3 reserve

reserve为字符串预留空间(即可以理解为需要多少空间提前开好,不用边用边开)

reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于 string的底层空间总大小时,reserver不会改变容量大小。(在vs2019下reserve只扩容不缩容,在g++下reserve会缩容,但只会缩到现有数据的大小)

用法如下:

int main()
{
	string s1("hello world!");
	cout << s1.capacity() << endl;
	s1.reserve(500);
	cout << s1.capacity() << endl;
	return 0;
}

4.4 resize

resize将有效字符的个数改变成n个,多出的空间用字符c填充

resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字 符个数增多时:resize(n)用'\0'来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大 小,如果是将元素个数减少,底层空间总大小不变。

①如果要扩容的空间>capacity,则扩容+尾插。验证如下:

int main()
{
	string s1("hello world!");
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	//如果要扩容的空间>capacity
	s1.resize(100);
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	cout << s1 << endl;
	return 0;
}

int main()
{
	string s1("hello world!");
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	//如果要扩容的空间>capacity
	s1.resize(100, 'x');
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	cout << s1 << endl;
	return 0;
}

 ②如果size<n<capacity,则只尾插不扩容,验证如下:

int main()
{
	string s1("hello world!");
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	//如果size<n<capacity
	s1.resize(13,'x');
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	cout << s1 << endl;
	return 0;
}

③如果n<size,只删除、保留前n个,不缩容,验证如下:

int main()
{
	string s1("hello world!");
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	//如果n<capacity
	s1.resize(6);
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	cout << s1 << endl;
	return 0;
}

(reserve与resize的区别可以概括为一句话:reserve只影响容量,不影响数据,resize既影响容量又影响数据)

4.5 clear

清空有效字符

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

验证如下:

int main()
{
	string s1("hello world!");
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	s1.clear();
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	cout << s1 << endl;
	return 0;
}

4.6 empty

检测字符串释放为空串,是返回true,否则返回false(注意:空为真,不空为假)

验证如下:

int main()
{
	string s1("hello world!");
	cout << s1.empty() << endl;
	string s2;
	cout << s2.empty() << endl;
	return 0;
}

5.string类对象的修改操作

修改操作无非就是增删查改,这里只介绍比较常用的操作

5.1 增

1. append

增操作其实已经有了一个我们熟悉的push_back,可是push_back每次只能尾插一个字符,为了方便就有了append:

比如我们二者兼用:

int main()
{
	string s1("hello");
	s1.push_back(' ');
	s1.append("world!");
	cout << s1 << endl;
	return 0;
}

append的其他常用用法如下:

①string& append (const char* s);插入常量字符串(上面的用法)

②string& append (const string& str);插入string:

int main()
{
	string s1("hello");
	string s2(s1);
	return 0;
}

③string& append (const string& str, size_t subpos, size_t sublen);插入string从subpos位置起到sublen个字符止的那部分:

int main()
{
	string s1("hello world!");
	string s2(s1,1,3);
	return 0;
}

④string& append (const char* s, size_t n);插入常量字符串的前n个:

int main()
{
	string s1("hello world!", 3);
	return 0;
}

⑥string& append (size_t n, char c);插入n个字符c:

int main()
{
	string s1(4,'x');
	return 0;
}

⑦string& append (InputIterator first, InputIterator last);插入string的一部分:

插入s1:

int main()
{
	string s1("hello world!");
	string s2;
	s2.append(s1.begin(), s1.end());
	return 0;
}

 插入去头去尾的s1:

int main()
{
	string s1("hello world!");
	string s2;
	s2.append(++s1.begin(), --s1.end());
	return 0;
}

2. +=

①string& operator+= (const string& str);插入string:

int main()
{
	string s1("hello world!");
	string s2;
	s2 += s1;
	return 0;
}

②string& operator+= (const char* s);插入字符串:

int main()
{
	string s1;
	s1 += "hello world!";
	return 0;
}

 ③string& operator+= (char c);插入字符:

int main()
{
	string s1;
	s1 += '!';
	return 0;
}

3.insert 

观察push_back与append,二者都是尾插,那有没有不是尾插的方法呢?当然有!

①string& insert (size_t pos, const string& str);在pos位置插入string:

int main()
{
	string s1("hello ");
	string s2("world!");
	s2.insert(0, s1);
	return 0;
}

②string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);在pos位置插入string的一部分:

int main()
{
	string s1("hello ");
	string s2("world!");
	s2.insert(0, s1, 1, 3);//ellworld!
	return 0;
}

③string& insert (size_t pos, const char* s);在pos位置插入字符串:

int main()
{
	string s1("world!");
	s1.insert(0, "hello ");//hello world!
	return 0;
}

④string& insert (size_t pos, const char* s, size_t n);在pos位置插入字符串的前n个:

int main()
{
	string s1("world!");
	s1.insert(0, "hello ",3);//helworld!
	return 0;
}

5.2 删

1. pop_back

尾删:

int main()
{
	string s1("hello world!");
	s1.pop_back();//hello world
	return 0;
}

2. erase

①string& erase (size_t pos = 0, size_t len = npos);从第pos个位置删除len个字符:

int main()
{
	string s1("hello world!");
	s1.erase(5, 1);//helloworld!
	return 0;
}

②iterator erase (iterator p);删除string的第p个位置:

int main()
{
	string s1("hello world!");
	s1.erase(s1.begin()+1);//hllo world!
	return 0;
}

5.3 查 

1.substr

string substr (size_t pos = 0, size_t len = npos) const;从pos位置开始,取len个字符:

int main()
{
	string s1("hello world!");
	string s2 = s1.substr(6, 5);//world
	return 0;
}

2.find

关于find的返回值:

The position of the first character of the first match.
If no matches were found, the function returns string::npos.
即:如果找到了就返回找到的第一个的下标,如果没有找到就返回整型的最大值。

①size_t find (char c, size_t pos = 0) const;从pos位置开始找c,没有给pos默认从头开始找:

int main()
{
	string s1("hello world!");
	size_t pos1 = s1.find('l');//2
	size_t pos2 = s1.find('l', 5);//9
	size_t pos3 = s1.find('x');//npos
	return 0;
}

 例:取string指定的一部分:

int main()
{
	//取文件名后缀
	string s1("test.cpp");
	size_t pos1 = s1.find('.');
	if (pos1 != string::npos)
	{
		/*string s2 = s1.substr(pos1, s1.size() - pos1);*/
		string s2 = s1.substr(pos1);
		cout << s2 << endl;
	}
	return 0;
}

5.4 改

1. replace

①string& replace (size_t pos, size_t len, const string& str);在pos位置的len个字符替换成str:

int main()
{
	string s1("hello world!");
	s1.replace(5, 1, "?");//hello?world!
	return 0;
}

2. swap 

与另一个string交换:

int main()
{
	string s1("hello world!");
	string s2;
	s2.swap(s1);//hello world!
	return 0;
}

5.5 查改结合完成替换操作

例:将s1的空格全部替换为?

方法一:

int main()
{
	string s1("have a good time!");
	cout << "替换前s1=" << s1 << endl;//have a good time!
	size_t pos = s1.find(' ', 0);
	while (pos != string::npos)
	{
		s1.replace(pos, 1, "?");
		pos = s1.find(' ', pos + 1);
	}
	cout << "替换后s1=" << s1 << endl;//have?a?good?time!
	return 0;
}

实际中replace效率太低,因此尽量少用replace

方法二:

int main()
{
	string s1("have a good time!");
	cout << "替换前" << s1 << endl;//have a good time!
	string s2;
	for (auto ch : s1)
	{
		if (ch == ' ')
		{
			s2 += "?";
		}
		else
		{
			s2 += ch;
		}
	}
	s1.swap(s2);
	cout << "替换后" << s1 << endl;//have?a?good?time!
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

模块一SpringBoot(一)

maven记得配置本地路径和镜像 IJ搭建 SpringIntiallizer--》将https://start.spring.io改成https://start.aliyun.com/ 项目结构 Spring有默认配置&#xff0c; application.properties会覆盖默认信息&#xff1a; 如覆盖端口号server.port8888

一个最简单的comsol斜坡稳定性分析例子——详细步骤

一个最简单的comsol斜坡稳定性分析例子——详细步骤 标准模型例子—详细步骤 线弹性模型下的地应力平衡预应力与预应变、土壤塑性和安全系数求解的辅助扫描

【深入理解JVM】关于Object o = new Object()

1. 解释一下对象的创建过程 “半初始化”状态通常指的是对象在内存分配后、但在完全初始化之前的一种状态。在Java中&#xff0c;虽然JVM的规范和设计努力避免对象处于这种不稳定的状态&#xff0c;但在多线程环境下&#xff0c;由于指令重排序等并发问题&#xff0c;仍有可能…

通义千问 2,大模型应用开发时的新选择

我在进行 AI 相关的开发中&#xff0c;最常用的模型是通义千问。本地开发的时候&#xff0c;使用 Ollama 来运行 qwen 模型。集成测试和线上环境&#xff0c;使用阿里云模型服务灵积上的通义千问模型。使用阿里云的好处是&#xff1a;模型服务的获取方便&#xff0c;稳定性好&a…

无人机5公里WiFi低延迟图传模组,抗干扰、长距离、低延迟,飞睿智能无线通信新标杆

在科技日新月异的今天&#xff0c;我们见证了无数通信技术的飞跃。从开始的电报、电话&#xff0c;到如今的4G、5G网络&#xff0c;再到WiFi的广泛应用&#xff0c;每一次技术的革新都极大地改变了人们的生活方式。飞睿智能5公里WiFi低延迟图传模组&#xff0c;它以其独特的优势…

GD32实战篇-双向数控BUCK-BOOST-BUCK降压理论基础

本文章基于兆易创新GD32 MCU所提供的2.2.4版本库函数开发 向上代码兼容GD32F450ZGT6中使用 后续项目主要在下面该专栏中发布&#xff1a; https://blog.csdn.net/qq_62316532/category_12608431.html?spm1001.2014.3001.5482 感兴趣的点个关注收藏一下吧! 电机驱动开发可以跳转…

Java多线程不会?一文解决——

方法一 新建类如MyThread继承Thread类重写run()方法再通过new MyThread类来新建线程通过start方法启动新线程 案例&#xff1a; class MyThread extends Thread {public MyThread(String name) {super(name);}Overridepublic void run() {for(int i0;i<10;i){System.out.…

kafka-3

Kafka 消费组 consumer-offsets-N 稀疏索引 Kafka集群 集群搭建 集群启动和验证 Topic的意义 Topic和Partition 分区 副本 集群操作指令 多分区&多副本 多分区消费组 Rebalance机制 Rebalance机制处理流程 Rebalance机制-Range Rebalance机制-RoudRobin Rebalance机制-St…

【Linux】在线求助命令--help,man page , info page

我们知道Linux有很多的命令&#xff0c;那LInux要不要背命令&#xff1f; 答案是背最常用的那些就行了 那有的时候我们想查询一些命令的详细用法该怎么办呢&#xff1f; 这里我给出3种方法 1.--help --help的使用方法很简单啊 要查询的命令 --help 我们看个例子 这里我只…

一览 Anoma 上的有趣应用概念

撰文&#xff1a;Tia&#xff0c;Techub News 本文来源香港Web3媒体&#xff1a;Techub News Anoma 的目标是为应用提供通用的意图机器接口&#xff0c;这意味着使用 Anoma&#xff0c;开发人员可以根据意图和分布式意图机编写应用&#xff0c;而不是根据事务和特定状态机进行…

java原子类

在Java中&#xff0c;原子类&#xff08;Atomic Classes&#xff09; 是位于java.util.concurrent.atomic包中的一组类&#xff0c;这些类提供了一些原子操作&#xff0c;用于在多线程环境下进行安全的并发编程。原子类利用了底层的硬件支持&#xff0c;确保操作的原子性和线程…

初阶数据结构 二叉树常用函数(二)

函数一 求二叉树第K层的节点个数 还是一样 我们假设 K就是等于一 如果说是一个空数的话就返回0 如果说有值的话就返回一个1就可以 假设这个这层既不为空 又不是第K层的话 那么就说明第K层肯定是子树下面 那么就说明是左右子树的第&#xff08;K-1&#xff09;层 那么只将…

谷哥剪映助手使用教程-剪映自动化批量视频剪辑软件-批量混剪素材替换

谷哥剪映助手是一款提高视频剪辑效率的软件&#xff0c;很多人问具体怎么使用&#xff0c;我会抽点时间把各个功能拆分开来&#xff0c;一个个介绍。 一、按组精准替换素材 该功能可以按组精确替换图片或视频素材&#xff0c;如果你草稿里只有一个素材需要替换&#xff0c;请…

【算法笔记自学】第 8 章 提高篇(2)——搜索专题

8.1深度优先搜索&#xff08;DFS&#xff09; #include <cstdio>const int MAXN 5; int n, m, maze[MAXN][MAXN]; bool visited[MAXN][MAXN] {false}; int counter 0;const int MAXD 4; int dx[MAXD] {0, 0, 1, -1}; int dy[MAXD] {1, -1, 0, 0};bool isValid(int …

docker中实现多机redis主从集群

redis主从集群是每个使用redis的小伙伴都必需知道的&#xff0c;那如何在docker中快速配置呢&#xff1f;这篇来教你快速上手&#xff0c;跟着复制完全就能用&#xff01;&#xff01; 1. 前置准备 1.1 docker安装 以防有小伙伴没预先安装docker&#xff0c;这里提供安装步骤…

驾校管理系统设计

驾校管理系统设计旨在提高驾校运营效率、学员管理、教练安排、考试预约、财务结算等方面的能力。以下是一个基本的设计框架&#xff0c;包括关键模块和数据表设计&#xff1a; 1. 系统架构设计 前端界面&#xff1a;提供给学员、教练和管理员使用的Web界面或移动应用&#xf…

CGAL计算凸包(OSG进行可视化)

目录 一、什么是凸包 二、运行步骤 1、安装依赖项 2、编译osg库 3、运行代码 4、运行截图 一、什么是凸包 凸包是计算几何中的一个基本概念,用来描述一个点集的最小凸包围形。具体来说,给定一个点集,凸包是包含该点集的最小凸多边形或凸多面体。 二维凸包:在二维平面…

# 三 JS的流程控制和函数

三 JS的流程控制和函数 3.1 JS分支结构 if结构 这里的if结构几乎和JAVA中的一样,需要注意的是 if()中的非空字符串会被认为是trueif()中的非零数字会被认为是true 代码 if(false){// 非空字符串 if判断为trueconsole.log(true) }else{console.log(false) } if(){// 长度为0…

昇思MindSpore学习笔记4-03生成式--Diffusion扩散模型

摘要&#xff1a; 记录昇思MindSpore AI框架使用DDPM模型给图像数据正向逐步添加噪声&#xff0c;反向逐步去除噪声的工作原理和实际使用方法、步骤。 一、概念 1. 扩散模型Diffusion Models DDPM(denoising diffusion probabilistic model) &#xff08;无&#xff09;条件…

数据库系统原理练习 | 作业1-第1章绪论(附答案)

整理自博主本科《数据库系统原理》专业课完成的课后作业&#xff0c;以便各位学习数据库系统概论的小伙伴们参考、学习。 *文中若存在书写不合理的地方&#xff0c;欢迎各位斧正。 专业课本&#xff1a; 目录 一、选择题 二&#xff1a;简答题 三&#xff1a;综合题 一、选择…