IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++STL详解(一):string类的介绍以及基本使用 -> 正文阅读

[C++知识库]C++STL详解(一):string类的介绍以及基本使用

为什么要学习string类

C语言的字符串

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

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

字符的一些补充知识

众所周知英文字符等符号在计算机中采用ASCII码的方式存储。

那么对于中文,日文,其他国家的文字计算机又该如何存储呢?对于中文等其他字符,ASCII码则表示不了,因为一个字节存不下,对于这些字符,则采用Unicode编码标准:它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。Unicode标准下每个中文是两个字节,这样就能存储大部分的中文了。

同时也有新的字符型wchar_t来存储两个字节的字符,它能更好的表示unicode编码

//char ascall编码->英文
//如何显示中文?日文?其他国家的文字计算机如何存储呢?unicode utf-8 utf-16 utf-32 gbk
int main()
{
	char ch1 = 'a';
	char ch2 = 97;
	cout << ch1 << endl;
	cout << ch2 << endl;

	char str1[] = "中国";
	cout << strlen(str1) << endl;


	wchar_t wch;//宽字节,2byte,能更好的表示unicode编码
	char ch;
	cout << sizeof(wch) << endl;
	cout << sizeof(ch) << endl;

	return 0;
}

在这里插入图片描述

在正式讲string之前我给大家推荐一个网站: string类

这个是C++的官方文档,如果对于C++STL容器或者其他不清楚的地方就可以去这上面查一下。

标准库中的string类

string类
  1. 字符串是表示字符序列的类
  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
  4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
  5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结:

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

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

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

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

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

string类对象的常见构造函数

在C++98中有以下7种构造函数

在这里插入图片描述

(constructor)函数名称功能说明
string() (重点)构造空的string类对象,即空字符串
string(const char* s) (重点)用C-string来构造string类对象
string(const stirng& s) (重点)拷贝构造函数
string(size_t n,char c)string类对象中包含n个字符c
string(const string& str, size_t pos, size_t len = npos)从str对象中的pos位置开始截取len个长度的字符,如果len>str的长度,那么截取完str就结束
string(const char* s,size_t n)从s指向的字符数组中复制前n个字符
int main()
{
	string s1;
	string s2("hello world");
	string s3(s2);
	string s4 = "hello world!!!";

	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;


	string s5(s4, 3, 5);
	cout << s5 << endl;

	char* url = "http://www.cplusplus.com/reference/string/string/string/";
	string s6(url, 4);
	cout << s6 << endl;


	string s7(10, 'x');
	cout << s7 << endl;

	s7 = s2;
	cout << s7 << endl;

	return 0;
}

在这里插入图片描述

string的三种遍历方式

一、下标[]

在这里插入图片描述

	string s2("hello world");
	
	//1.下标[]
	for (size_t i = 0; i < s2.size(); i++)
	{
		cout << s2[i] << " ";
	}
	cout << endl;

在这里插入图片描述

这里的size()是一个公共成员函数,返回string类对象的长度,和length()相同

在这里插入图片描述

通过上面图片可以看到库里面重载了两个operator[]函数,一个是可读可写的,一个是只读的。因此这里不光可以遍历s2,还可以修改s2里面的值。

	string s2("hello world");

	//三种遍历
	//1.下标[]
	for (size_t i = 0; i < s2.size(); i++)
	{
		cout << s2[i] << " ";
	}
	cout << endl;

	for (size_t i = 0; i < s2.size(); i++)
	{
		//s2.operator[](i)
		s2[i] = 'x';
	}
	cout << endl;

	for (size_t i = 0; i < s2.size(); i++)
	{
		cout << s2[i] << " ";
	}
	cout << endl;

在这里插入图片描述

二、迭代器遍历

迭代器iterator是string中的类,所以要加域作用限定符

    string s2("hello world");
    string::iterator it = s2.begin();
	while (it != s2.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

在这里插入图片描述

[begin(),end()) end()返回的不是最后一个数据的位置,返回的是最后一个数据的下一个位置,需要要注意的是,C++中凡是给迭代器一般都是给的[)左闭右开的区间。

这个时候可能就会有人问了:那么迭代器的意义是什么呢?

迭代器意义:像string,vector支持[]遍历,但是list,map等等容器不支持下标[]我们就要用迭代器遍历,所以迭代器是一种统一使用的方式。

	vector<int> v = { 1, 2, 3, 4 };
	vector<int>::iterator vit = v.begin();
	while (vit != v.end())
	{
		cout << *vit << " ";
		++vit;
	}
	cout << endl;

	list<int> lt = { 1, 2, 3, 4 };
	list<int>::iterator ltit = lt.begin();
	while (ltit != lt.end())
	{
		cout << *ltit << " ";
		++ltit;
	}
	cout << endl;

在这里插入图片描述

反向迭代器

    //反向迭代器
	string s3("123456");
	string::iterator it3 = s3.begin();
	while (it3 != s3.end())
	{
		cout << *it3 << " ";
		++it3;
	}
	cout << endl;

	string::reverse_iterator rit = s3.rbegin();
	while (rit != s3.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;

在这里插入图片描述

三、范围for

C++11中提供范围for,特点:写起来简洁

依次取容器中的数据,赋值给e,自动判断结束

范围for实现的本质就是迭代器,因此也支持其他容器的遍历

    string s3("123456");
    vector<int> v = { 1, 2, 3, 4 };
    list<int> lt = { 1, 2, 3, 4 };
    for (auto&e : s3)
	{
		e += 1;
	}
	cout << endl;

	for (auto e : s3)
	{
		cout << e << " ";
	}
	cout << endl;

	for (auto x : v)
	{
		cout << x << " ";
	}
	cout << endl;
	
	for (auto x : lt)
	{
		cout << x << " ";
	}
	cout << endl;

在这里插入图片描述

string类对象的修改操作

在这里插入图片描述

函数名称功能说明
operator+=str(重点)在字符串末尾追加一个字符串
push_back(ch)在字符串末尾插入一个字符
append(str)在字符串末尾追加一个字符串
insert(pos,str) insert(pos,n,ch)在pos位置插入一个字符串,在pos位置插入n个ch字符。尽量少用insert,因为底层实现是数组,头部或者中间插入需要挪动数据
erase(pos,len)从pos位置开始删除len个长度的字符 如果不给pos与len则默认从开头的位置向后全删除
pop_back(ch)在字符串末尾删除一个字符
int main()
{
	string s1;
	s1.push_back('h');
	s1.push_back('e');
	s1.push_back('l');
	s1.push_back('l');
	s1.push_back('o');

	s1.append("world");
	cout << s1 << endl;

	string s2("!!!");
	//s1.append(s2);
	s1.append(s2.begin(), s2.end());
	cout << s1 << endl;

	//实际中最喜欢用这个+=
	s1 += ' ';
	s1 += "凌峰";
	s1 += s2;
	cout << s1 << endl;

	//尽量少用insert,因为底层实现是数组,头部或者中间插入需要挪动数据
	s1.insert(0, "x");
	cout << s1 << endl;
	s1.insert(3, "yyyy");
	cout << s1 << endl;
	s1.insert(0, "yyyy");
	cout << s1 << endl;

	s1.erase(0, 1);
	cout << s1 << endl;

	s1.erase(0, 3);
	cout << s1 << endl;

	s1.erase(3, 10);
	cout << s1 << endl;

	s1.erase(3);
	cout << s1 << endl;

}

在这里插入图片描述

string类对象的容量操作
函数名称功能说明
size(重点)返回字符串有效字符长度
length返回字符串有效字符长度
capacity返回空间总大小
empty (重点)检测字符串是否为空串,是返回true,否则返回false
clear (重点)清空有效字符(’\0’不是有效字符,它是标识字符)
reserve (重点)为字符串预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。
resize (重点)将有效字符的个数变成n个,多出的空间用字符c填充,如果不给字符c的话默认是’\0’。如果有效元素的个数是变大的,那么此时capacity有可能会改变,如果是有效元素的个数是减少的,那么capacity大小不变。
    string s1;
	cout << "size:" << s1.size() << endl;
	cout << "capacity:" << s1.capacity() << endl;
	cout << s1 << endl;

	s1.resize(20, 'x');
	cout << "size:" << s1.size() << endl;
	cout << "capacity:" << s1.capacity() << endl;
	cout << s1 << endl;

	string s2("hello world");
	s2.resize(20, 'x');
	cout << s2 << endl;
	cout << "size:" << s2.size() << endl;
	cout << "capacity:" << s2.capacity() << endl;

	s2.resize(5);
	cout << s2 << endl;
	cout << "size:" << s2.size() << endl;
	cout << "capacity:" << s2.capacity() << endl;


	string s3;
    s3.resize(10);
	cout << "size:" << s3.size() << endl;
	cout << "capacity:" << s3.capacity() << endl;

	s3.reserve(40);
	cout << "size:" << s3.size() << endl;
	cout << "capacity:" << s3.capacity() << endl;

在这里插入图片描述

下面我们再来通过代码来看一下resize与reserve的区别

	string s4;
	s4.resize(127);
	int oldCp = s4.capacity();
	for (char ch = 0; ch < 127; ++ch)
	{
		s4 += ch;
		if (oldCp != s4.capacity())
		{
			cout << "增容:" << oldCp << "->" << s4.capacity();
			oldCp = s4.capacity();
		}
	}
	cout << s4 << endl;

在这里插入图片描述

resize是把对象的有效字符个数变成n个,而+=ch是在对象的末尾追加字符ch,因此后面空间不够了还是需要增容的。

	string s4;
	s4.reserve(127);
	//s4.resize(127);
	int oldCp = s4.capacity();
	for (char ch = 0; ch < 127; ++ch)
	{
		s4 += ch;
		if (oldCp != s4.capacity())
		{
			cout << "增容:" << oldCp << "->" << s4.capacity();
			oldCp = s4.capacity();
		}
	}
	cout << s4 << endl;

在这里插入图片描述

reserve是为string对象预留空间,我们如果知道这次操作需要多大的空间,那么只需要reserve一下我们就不需要增容了。

string类对象的查找等一些其他操作
函数名称功能说明
find + npos(重点)从字符串pos位置开始从前往后找一个字符或者字符串,返回该字符或者字符串第一次在当前字符串中出现的位置
rfind从字符串pos位置开始从后往前找一个字符或者字符串,返回该字符或者字符串第一次在当前字符串中出现的位置
c_str(重点)返回C格式字符串
substr在str中从pos位置开始,截取n个字符,然后将其返回
getline(非成员函数)获取一行字符串
operator>>(非成员函数) (重点)输入运算符重载
operator<<(非成员函数) (重点)输出运算符重载

下面再来说一下string的成员函数c_str()

    string s1("hello world");
	cout << s1 << endl; //调用operator<<(cout,s1)
	cout << s1.c_str() << endl;//调用operator<<(cout,const char* str)

	s1.resize(20);
	s1 += "!!!!";
	cout << s1 << endl;//调用operator<<(cout,s1)
	cout << s1.c_str() << endl;//调用operator<<(cout,const char* str)

	cout << strlen(s1.c_str()) << endl;
	cout << s1.size() << endl << endl;
  • 调用内置类型的operator<<(cout,const char* str)输出字符串的时候遇到’\0’就停止了,
  • 调用自定义类型的operator<<(cout,const string& s) 输出字符串的时候遇到‘\0’不会停止,它会将该字符串的内容全部都打印出来

在这里插入图片描述

大家可能会比较好奇string的find操作运用在哪些场景下呢?

一、假设要求取出文件名的后缀

	//假设要求取出文件名的后缀
	//string filename = "test.txt";
	//size_t pos = filename.find('.');

	string filename = "test.txt.zip";
	size_t pos = filename.rfind('.');
	if (pos != string::npos)
	{
		//string suff(filename, pos, filename.size() - pos);
		string suff(filename, pos);
		cout << suff << endl;
	}

在这里插入图片描述

二、要求写一个程序分别取出域名和协议名

//取出域名
string GetDomain(const string& url)
{
    //查找"://",找到返回该字符串的起始下标位置
	size_t pos = url.find("://");
	if (pos != string::npos)
	{
		size_t start = pos + 3;
		size_t end = url.find('/',start);
		if (end != string::npos)
		{
            //由于是左闭右开区间[start,end),所以只需要end-start就能计算出域名的长度
			return url.substr(start, end - start);
		}
		else
		{
            //找不到则返回空串
			return string();
		}

	}
	else
	{
        //找不到则返回空串
		return string();
	}
}

//取出协议名
string GetProtocol(const string& url)
{
     //查找"://",找到返回该字符串的起始下标位置
	size_t pos = url.find("://");
	if (pos != string::npos)
	{
        //由于是左闭右开区间[0,pos),所以只需要pos - 0就能计算出协议名的长度
		return url.substr(0, pos - 0);
	}
	else
	{
        //找不到则返回空串
		return string();
	}
}

int main()
{
    //要求写一个程序分别取出域名和协议名
	string ur11 = "http://www.cplusplus.com/reference/string/string/rfind/";
	string ur12 = "https://tower.im/users/sign_in";
	string ur13 = "tower.im/users/sign_in";

	cout << GetDomain(ur11) << endl;
	cout << GetProtocol(ur11) << endl;

	cout << GetDomain(ur12) << endl;
	cout << GetProtocol(ur12) << endl;
}

在这里插入图片描述

如果我们想输入"asadg bc"这样的字符串应该怎么办呢?

scanf,cin,gets这些好像都不能帮我们解决问题,因为当他们读到空格或者回车键的时候就会停止读取

那么有什么办法来帮我们读取它呢

下面再来说一个getline函数:获取一行字符串

int main()
{
	string s1;
	getline(cin,s1);
	cout << s1 << endl;
}

在这里插入图片描述

可以看到通过使用getiline函数就达到了我们想要的结果

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-01-24 10:38:58  更:2022-01-24 10:41:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:46:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码