【C++初阶】六、STL---string

228 阅读8分钟

目录

一、为什么学习string类

1.1 C语言中的字符串

1.2 字符串在OJ面试中

二、标准库中的string类

2.1 string 介绍

2.2 编码格式

三、string 类常用接口

3.1 Member functions(成员函数)

3.1.1 构造函数(Construct)

3.1.2 赋值重载(operator=) ​编辑

3.1.3 string类对象的容量操作(Capacity)

3.1.4 string类对象的访问(Element access) 

3.1.5 string对象的访问及遍历操作(Iterators)  

3.1.6 string类对象的修改(Modifiers) 

3.1.7 string类对象的操作(String operations)

3.2 string类非成员函数(Non-member function overloads) 


一、为什么学习string类

1.1 C语言中的字符串

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

1.2 字符串在OJ面试中

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

注:由于历史原因,string类并不归属到 STL 之中,但是 string类与 STL 很相像,就把 string类归属到 STL 中一起学习

学习 STL 一定要多翻阅 STL的官方文档查看详细解释 C++官网cppreference

但是官网的排布内容不是很好,推荐使用这个非官网的网站:cplusplus ,右上角切换到旧版体验更好

二、标准库中的string类

2.1 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 ,以及多翻阅参考cplusplus 的标准文档 string

  • 这里为什么是 而没有 .h呢?
  • 因为 C语言中已经有一个 <string.h>了,为了避免冲突和进行区别,所以使用 作为头文件

2.2 编码格式

string 是 basic_string 类模板使用字符类型 char 实例化得到的一个类

  

而且还以模板生成几个名字不同 string 类,这些类的主要区别在于编码方式的不同,链接

string 为什么要设计成模板呢? 

因为编码格式的不同,一些字符存储所占用的空间不同

常见的一些编码格式:

(1)ASCII码,是美国设计的;ASCII码表是:计算机当中存的值,和字符的映射,但是只有256个字符的表示,用char表示

(2)Unicode,也叫做万国码。,Unicode是针对全世界的语言而设计的一种编码,常见的有utf-8 utf-16 utf-32

(3)gbk,gbk是叫做**国标码,**是针对中文创建的一个编码

 编码方式对 basic_string 的影响如下

  • char – 采用单字节编码;
  • char16_t – 采用双字节编码
  • char32_t – 采用四字节编码

这里我们以第一种进行学习,单字节编码格式的 string 

平时使用的 string 本质上是 basic_string, string 内部进行了 typedef

typedef basic_string<char, char_traits, allocator> string

 -------------------我是分割线------------------  

下面介绍string类常用的接口 ,要熟练掌握,其余的用时查阅即可

三、string 类常用接口

3.1 Member functions(成员函数)

3.1.1 构造函数(Construct)

提供了很多构造函数,只需要掌握其中最常用的几个就可以了,其余的如果有需要再查询文档

(constructor)函数名称函数功能
string() (重点)构造空的string类对象,即空字符
string(const char* s) (重点)用字符数组来构造string类对象
string(const string&s) (重点)拷贝构造函数
string(size_t n, char c)用n个c字符来构造string类对象

测试代码

void Test_string()
{
	string s1; // 构造空的string类对象s1
	cout << "s1: " << s1 << endl;
	string s2("hello world"); // 用C格式字符串构造string类对象s2
	cout << "s2: " << s2 << endl;
	string s3(s2); // 拷贝构造s3
	cout << "s3: " << s3 << endl;
}

 运行结果

3.1.2 赋值重载(operator=) 

测试代码

void TestString()
{
	string s1("hello");
	string s2, s3, s4;
	s2 = s1;
	s3 = "world";
	s4 = s2 + s3;

	cout << "s2: " << s2 << endl;
	cout << "s3: " << s3 << endl;
	cout << "s4: " << s4 << endl;
}

运行结果​至于析构函数(destructor)则简单了解即可,因为程序结束自动调用析构函数 

3.1.3 string类对象的容量操作(Capacity

​掌握最常用的几个就可以了,其余的如果有需要再查询文档

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

(1)size && length

        size 和 length 的功能完全相同,这是早期 string 设计遗留的问题

测试代码

void TestString()
{
	string s1("hello");
	cout << "s1.size: " << s1.size() << endl;
	cout << "s1.length: " << s1.length() << endl;
	cout << "s1.capacity: " << s1.capacity() << endl;
 
	string s2("aaaaa");
	cout << "s2: " << s2 << endl;
	cout << "s2.empty: " << s2.empty() << endl;
	s2.clear();//清空有效字符串
	cout << "s2.clear后: " << s2 << endl;
}

运行结果

(2)resize

        resize 函数用来调整字符串大小,它一共分为三种情况: 

  1. n 小于原字符串的 size,此时 resize 函数会将原字符串的 size 改为 n,但不会改变 capacity
  2. 大于原字符串的 size,但小于其 capacity,此时 resize 函数会将 size 后面的空间全部设置为字符 c 或默认的‘\0’
  3. 大于原字符串的 capacity,此时 resize 函数会将原字符串扩容,然后将size 后面的空间全部设置为字符 c 默认的‘\0’  

测试代码

void TestString()
{
	string s1("hello world!");
	cout << "s1: " << s1 << endl;
	cout << "s1.size: " << s1.size() << endl;
	cout << "s1.capacity: " << s1.capacity() << endl;
	s1.resize(20);
	cout << "s1: " << s1 << endl;
	cout << "s1.size: " << s1.size() << endl;
	cout << "s1.capacity: " << s1.capacity() << endl;
	s1.resize(15);
	cout << "s1: " << s1 << endl;
	cout << "s1.size: " << s1.size() << endl;
	cout << "s1.capacity: " << s1.capacity() << endl;
	s1.resize(5);
	cout << "s1: " << s1 << endl;
	cout << "s1.size: " << s1.size() << endl;
	cout << "s1.capacity: " << s1.capacity() << endl;
}

运行结果

(3) reserve

        reserve 用来扩容与预留空间,相当于C语言中的 realloc 函数,它分两种情况:

  • n 大于原字符串的 capacity,此时 reserve 函数会将 capacity 扩容到 n
  • n 小于等于原字符串的 capacity,capacity 容量不变 (不缩容)

注意区分 reserve 和 reverse 

测试代码

void TestString()
{
	string s1("hello world!");
	cout << "s1: " << s1 << endl;
	cout << "s1.size: " << s1.size() << endl;
	cout << "s1.capacity: " << s1.capacity() << endl << endl;
	
	s1.reserve(5);
	s1.reserve(15);
	cout << "s1: " << s1 << endl;
	cout << "s1.size: " << s1.size() << endl;
	cout << "s1.capacity: " << s1.capacity() << endl << endl;

	s1.reserve(30);
	cout << "s1: " << s1 << endl;
	cout << "s1.size: " << s1.size() << endl;
	cout << "s1.capacity: " << s1.capacity() << endl;
}

 运行结果

3.1.4 string类对象的访问(Element access) 

掌握 operator[] 使用即可,其它也是使用到了再查询文档

测试代码

void TestString()
{
	string s1 = "hello world!";

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

}

 运行结果

3.1.5 string对象的访问及遍历操作(Iterators)  

        Iterators 是 C++中的迭代器,可以把它当成指针来理解(string 和 vector),不是所有迭代器都是指针

也是老样子,掌握常用的几个即可,其它使用到再查询文档

函数名称函数功能
begin()返回一个指向字符串中第一个字符的迭代器
end()返回一个指向字符串最后一个字符下一个位置(‘\0’)的迭代器
rbegin()反向迭代器,返回一个指向字符串最后一个字符下一个位置(‘\0’)的迭代器
rend()反向迭代器,返回一个指向字符串中第一个字符的迭代器

测试代码

void TestString()
{
	string s1 = "12345";
	string::iterator it = s1.begin();
	while (it < s1.end())
	{
		(*it)++;
		cout << *it;
		++it;
	}
}

 运行结果

        反向迭代器就是正向迭代器反过来使用,不再演示。实际上,范围 for 的底层就是使用迭代器实现的

3.1.6 string类对象的修改(Modifiers) 

也是老样子,掌握常用的几个即可,其它使用到再查询文档

函数名称功能说明
push_back在字符串后尾插字符c
append在字符串后追加一个字符串
operator+=(重点)在字符串后追加字符串str

(1) operator+=

        operator+= 是运算符重载,用于向字符串尾插数据,支持尾插一个字符串以及尾插一个字符

测试代码

void TestString()
{
	string s1 = "hello";
	string s2 = "world";

	s1 += s2;
	cout << s1 << endl;
}

 运行结果

(2)push_back 、append

push_back 是在字符串后尾插字符c,append 是在字符串后追加一个字符串,这些都与 += 类似,设计有些多余,不演示了

(3)insert 和 erase

        insert 函数用于向在字符串的 pos 处插入数据,erase 用来从 pos 位置开始向后删除 len 个字符,因为时间复杂度的问题,很少使用,有需要直接看文档,不演示了

3.1.7 string类对象的操作(String operations

老规矩,掌握常用的几个即可,其它使用到再查询文档

函数名称功能说明
c_str(重点)返回C格式字符串
find(重点)从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr在str中从pos位置开始,截取n个字符,然后将其返回

(1)c_str

        在某些场景中只支持对C形式的字符串,即字符数组进行操作,比如网络传输、fopen,而不支持对C++中的 string 对象进行操作,所以 string 提供了 c_str,用于返回C形式的字符串,即以 ‘\0’ 结尾的字符串

测试代码

void TestString()
{
	string s1 = "hello world";
	const char* str = s1.c_str();

	cout << str << endl;
}

 运行结果

(2)find

        find 用于返回 一个字符或一个字符数组或一个string对象 在 string 中首次出现的位置,如果找不到就返回 npos: 

说明一下 npos,npos 是一个无符号数,npos = -1 即代表无符号的最大取值

测试代码

void TestString()
{
	string s1 = "hello world";

	cout << s1.find('o') << endl;
	cout << s1.find("or") << endl;
	cout << s1.find("a") << endl;
}

运行结果

(3)rfind

        rfind 是从末尾开始找指定内容,与 find 查找方向相反,不演示了

(4)substr

        substr 返回一个新构造的字符串对象,从字符位置 pos 开始,跨越 len 个字符或直到字符串结尾

测试代码

void TestString()
{
	string s1 = "hello world!!";

	cout << s1.substr(0, 5) << endl;
	cout << s1.substr(6, 6) << endl;
}

 运行结果

3.2 string类非成员函数(Non-member function overloads) 

掌握常用的几个即可,其它使用到再查询文档

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

(1) relational operators

两个 string 对象之间的大小关系函数重载,直接使用就好了,不演示了

(2) operator>> 和 operator<<

流插入与流提取运算符重载,这两个重载的作用就是为了让 string 对象支持直接输出和输入

(3)getline

         C语言中的 scanf 函数和 C++ 中的 cin 都是以空格、换行等作为不同数据之间的分割标志的,即当它们遇到这些符号时就会停止读。C语言提供了 gets 函数来读取一行字符,C++ 则是提供了 getline 函数来读取一行字符,并且我们还可以自己指定结束标志符,不演示了  

----------------我是分割线---------------

文章到这里就结束了,下一篇即将更新