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】中

1. 为什么学习string类?

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

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

2. 标准库中的string类

2.1 string类的常用接口说明

2.1.1 string类对象的常见构造

(constructor)函数名称功能说明
string() (重点)构造空的string类对象,即空字符串
string(const char* s) (重点)用C-string来构造string类对象
string(size_t n, char c)string类对象中包含n个字符c
string(const string&s) (重点)拷贝构造函数
int main()
{
	//string(); 
	string s1;	//构造一个空字符串
	
	//string(const char* s); 
	string s2("hello world");//复制s的内容
	
	//string(const string& str);
	string s3(s2);// 构造一个str复制品

	//string (const string& str, size_t pos, size_t len = npos);
	string s5(s2, 2);//从下标2后取全部的字符,进行初始化s5
	string s6(s2, 2, 100);//从下标2后取100个字符,字符不够停止取


	//string (const char* s, size_t n);
	string s7("hello world", 3);//复制s所指字符序列的前n个字符
	
	//string (size_t n, char c);
	string s8(10, '!');//生成n个c字符的字符串

	return 0;
}

2.1.2 string类对象的容量操作

函数名称功能说明
size(重点)返回字符串有效字符长度
length返回字符串有效字符长度
capacity返回空间总大小
empty (重点)检测字符串释放为空串,是返回true,否则返回false
clear (重点)清空有效字符
reserve (重点)为字符串预留空间
resize (重点)将有效字符的个数该成n个,多出的空间用字符c填充
size、length、max_size、capacity
void test1()
{
	string s1;
    cin >> s1;

	// 不包含最后作为结尾标识符的\0 , 有效字符的长度
	cout << s1.size() << endl;
	cout << s1.length() << endl;
	// 字符串最大容量
	cout << s1.max_size() << endl;
	cout << s1 << endl;
	// 能存储有效字符的容量大小,不包含'\0'
	cout << s1.capacity() << endl;
	// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小
	s1.clear();
	cout << s1 << endl;
	cout << s1.capacity() << endl;
	return ;
}
resize
void resize (size_t n);
void resize (size_t n, char c);

resize规则:
?1、当n大于对象当前的size时,将size扩大到n,扩大的容量用字符c填充,若c未给出,则默认为’\0’。
?2、当n小于当前对象的size时,将size缩小到n。
?3、若n大于当前对象capacity,则会先扩容后填充。


void test2()
{
	string s1;
	
	// 将s中有效字符个数增加到10个,多出位置用'x'进行填充
	s1.resize(10, 'x');//"xxxxxxxxxx"
	// 将s中有效字符个数增加到16个,多出位置用'\0'进行填充
	s1.resize(16);//"xxxxxxxxxx\0\0\0\0\0\0"
	// 将s中有效字符个数缩小到5个
	s1.resize(5);
	cout << s1.capacity() << endl;// 不会改变容量大小
	cout << s1.size() << endl;
 }
reserve
void reserve (size_t n = 0);

reserve 规则:
?1、当n大于对象当前的capacity时,将capacity扩大到n或大于n。
?2、当n小于对象当前的capacity时,什么也不做。

void test3()
{
	string s("string");
	cout << s << endl; //string
	cout << s.size() << endl; //4
	cout << s.capacity() << endl; //15

	s.reserve(18);//扩大到大于或等于18
	cout << s << endl; //string
	cout << s.size() << endl; //4
	cout << s.capacity() << endl; //31

	//reverse(n)当n小于对象当前的capacity时,什么也不做
	s.reserve(2);
	cout << s << endl; //string
	cout << s.size() << endl; //4
	cout << s.capacity() << endl; //31	
} 

reserve的增容情况

void TestPushBackReserve()
{
	string s;
	size_t sz = s.capacity();
	cout << "making s 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';
	}
}

注意:vs下是按照1.5倍增长的,根据环境有关。

reserve提高效率

// 利用reserve提高插入数据的效率,避免增容带来的开销
void TestPushBack()
{
	string s;
	s.reserve(100);//提前增容
	size_t sz = s.capacity();
	cout << "making s 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';
	}
}

小结

  1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一
    致,一般情况下基本都是用size()。
  2. clear()只是将string中有效字符清空,不改变底层空间大小。
  3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字
    符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的
    元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大
    小,如果是将元素个数减少,底层空间总大小不变。
  4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于
    string的底层空间总大小时,reserver不会改变容量大小。

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

函数名称功能说明
operator[] (重点)返回pos位置的字符,const string类对象调用
begin+ end begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
rbegin + rendbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
范围forC++11支持更简洁的范围for的新遍历方式
operator[]与at()
char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;

char& at (size_t pos);
const char& at (size_t pos) const;
// 底层原理
char& operator[] (size_t pos)
{
    assert(pos < _size);
	return _str[pos];
}

用法:

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

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

	cout << s1 << endl;

	for (size_t i = 0; i < s1.size(); ++i)
	{
		s1.at(i) -= 1;
	}
	cout << endl;
	cout << s1 << endl;

	//s1[100];
	try
	{
		s1.at(100);
	}
	catch (exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

operator[]与at()的区别
operator对非法访问进行断言处理,at()会抛异常。
注意:返回值是引用,我们可以对返回值修改读取。

begin与end函数

begin函数:返回一个指向字符串第一个字符的迭代器。

函数原型:

?iterator begin();
?const_iterator begin() const;

end函数:返回一个指向字符串结束字符的迭代器,即’\0’。
函数原型:

?iterator end();
?const_iterator end() const;

使用迭代器访问对象中的元素

void test_string1()
{
	string s1("hello world");

	// 方式2:迭代器
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		*it -= 1;
		++it;
	}
	cout << endl;
// 反向迭代器
// string::reverse_iterator it = s1.rbegin();
	auto it = s1.rbegin();
	while (it != s1.rend())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

使用范围for访问对象中的元素

void test_string1()
{
	string s1("hello world");
	// 方式3:范围for,自动往后迭代,自动判断结束
	// C++11
	//for (char& e : s1)
	for (auto& e : s1)
	{
		e -= 1;
	}
	for (auto e : s1)
	{
		cout << e << " ";
	}
	cout << endl;
}

注意:如果要修改数据,必须是引用,不然只是对临时变量进行修改。
范围for的底层其实就是迭代器访问。

const迭代器用法:

void func(const string& s)
{
	//string::const_iterator it = s.begin();
	auto it = s.begin();
	while (it != s.end())
	{
		//*it -= 1;
		cout << *it << " ";
		++it;
	}
	cout << endl;

	//auto rit = s.rbegin();
	string::const_reverse_iterator rit = s.rbegin();
	while (rit != s.rend())
	{
		//*rit = 'A';
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
}

cbegin(),cend(),crbegin(),crend(),这些函数返回const正/反迭代器。目的是为了提高可读性。

2.1.4 string类对象的修改操作

函数名称功能说明
push_back在字符串后尾插字符c
append在字符串后追加一个字符串
operator+= (重点)在字符串后追加字符串str
c_str(重点)返回C格式字符串
find + npos(重点)从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr在str中从pos位置开始,截取n个字符,然后将其返回
c_str()
const char* c_str() const;

返回一个指向数组的指针,该数组包含一个以空字符结尾的字符序列(即一个 C 字符串),表示字符串对象的当前值。此数组包含构成字符串对象值的相同字符序列,以及末尾的附加终止空字符 ( ‘\0’ )。

void test()
{
	string file("test.txt");
	FILE* fout = fopen(file.c_str(), "w");
}
substr()
string substr (size_t pos = 0, size_t len = npos) const;

生成字串,返回一个新建的细绳对象,其值初始化为此对象的子字符串的副本。
子字符串是从字符位置pos开始并跨越len个字符(或直到字符串末尾,以先到者为准)的对象部分。

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s1("BBQ321");
	string s2;
	s2 = s1.substr(2, 4);
	cout << s2 << endl; //Q321
	return 0;
}

find函数 & rfind 函数 & npos
size_t find (const string& str, size_t pos = 0) const;	//从POS位置开始查找str

size_t find (const char* s, size_t pos = 0) const;//找s字符串从POS位置开始找。

size_t find (const char* s, size_t pos, size_t n) const;//查找s的前n个字符在string pos开始位置找

size_t find (char c, size_t pos = 0) const;//查找c字符,从pos位置开始

返回值
第一个匹配的第一个字符的位置。
如果未找到匹配项,则该函数返回string::npos。
静态常量 size_t npos = -1;
size_t 的最大值npos是一个静态成员常量值,对于size_t类型的元素具有最大可能值。
此值在用作string的成员函数中的len(或sublen)参数的值时,表示“直到字符串的结尾”。 作为返回值,它通常用于表示不匹配。 此常量定义为-1值,因为size_t是无符号整数类型,所以它是该类型的最大可能表示值。
例子代码:

void test_string4()
{
	// 要求你取出文件的后缀名
	size_t pos = file.find('.');
	if (pos != string::npos)
	{
		string suffix = file.substr(pos, file.size() - pos);
		string suffix = file.substr(pos);
		cout << suffix << endl;
	}
========================================================================================	
	//提取链接内容
	string url("http://www.cplusplus.com/reference/string/string/find/");
	size_t pos1 = url.find(':');
	string protocol = url.substr(0, pos1 - 0);
	cout << protocol << endl;//http

	size_t pos2 = url.find('/', pos1+3);//从pos1+3位置开始找‘/’
	string domain = url.substr(pos1 + 3, pos2 - (pos1 + 3));
	cout << domain << endl;//www.cplusplus.com

	string uri = url.substr(pos2 + 1);
	cout << uri << endl;//reference/string/string/find/

}

insert()
string& insert (size_t pos, const string& str);//在POS位置插入
string& insert (size_t pos, const char* s);
iterator insert (iterator p, char c);
void test6()
{
	string s("abcd");
	string tmp("BBQ");
	s.insert(0,2,'g');//ggabcd
	s.insert(s.begin(), 'h');//hggabcd
	s.insert(0, "test");//testhggabcd
	s.insert(0, tmp);//BBQtesthggabcd
	cout << s;
}

注意:
避免中间位置插入,因为要挪动数据,效率低,o(N),尽量少用。

erase

string& erase (size_t pos = 0, size_t len = npos);//从POS位置开始删len长度,默认删到末尾
iterator erase (iterator p);//删除迭代器指向的内容
iterator erase (iterator first, iterator last);//删除迭代器区间的内容
void test_string6()
{
	string s("hello world");
	// 尽量少用头部和中间删除,因为要挪动数据,效率低
	s.erase(0, 1);
	s.erase(s.size() - 1, 1);
	cout << s << endl;
	s.erase(3);
	cout << s << endl;
}

注意:迭代器删除不常用。

append & operator+=
void Teststring()
{
	string str;
	str.push_back(' '); // 在str后插入空格
	str.append("hello"); // 在str后追加一个字	符"hello"
	str += 'B'; // 在str后追加一个字符'B'
	str += "BQ"; // 在str后追加一个字符串"BQ"
	cout<<str<<endl;//helloBBQ
}

小结:

  1. 在string尾部追加字符时,s.push_back( c ) / s.append(1, c) / s += 'c’三种的实现方式差不多,一般
    情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
  2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

2.1.5 string类非成员函数

函数功能说明
operator+尽量少用,因为传值返回,导致深拷贝效率低
operator>>(重点) 输入运算符重载
operator<<(重点) 输出运算符重载
getline (重点)获取一行字符串
relational operators(重点) 大小比较(关系运算符)
getline()
istream& getline (istream& is, string& str, char delim);
istream& getline (istream& is, string& str);

从is 中 提取字符并将它们存储到str中,直到找到分隔符delim(或换行符’\n’,对于(2))。

getline原理:

int main()
{
	string s;
	char ch;
	cin >> ch;
	//char ch = cin.get();
	//char ch = getchar();
	while (ch != '\n')
	{
		s += ch;
		//ch = getchar();
		cin >> ch;
	}
	cout <<s<<endl;
	return 0;
}

getline用法:

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s1,s2;
	getline(cin, s1); //输入:hello BBQ
	cout << s1 << endl; //输出:hello BBQ
	//对比
	cin>>s1;//输入: hello world
	cout<< s2 <<endl;// 输出 hello
	return 0;
}

用法二、

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s;
	getline(cin, s, 'Q'); //输入:hello BBQ
	cout << s << endl; //输出:hello BB
	return 0;
}

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-20 18:34:21  更:2022-07-20 18:38:08 
 
开发: 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年5日历 -2024/5/10 23:51:27-

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