您现在的位置是:首页 >技术杂谈 >C++ [STL之string的使用]网站首页技术杂谈

C++ [STL之string的使用]

ARMCSKGT 2023-06-11 20:00:02
简介C++ [STL之string的使用]

标题图

本文已收录至《C++语言和高级数据结构》专栏!
作者:ARMCSKGT

在这里插入图片描述


前言

字符串在程序中经常出现,C语言为此提供了很多字符串操作函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问,于是STL单独为字符串实现了一个容器,用来专门存储操作字符串,本文将介绍string的常用接口,有了string的加入,我们对字符串的操作将游刃有余!


正文

编码


我们知道计算机是美国最先研究的,为了描述字符出台ASCII((American Standard Code for Information Interchange)美国信息交换标准代码)编码,在计算机存储和显示英文信息,是美国标准,ASCII仅需 1Byte 就能存储,所以计算机中字符类型为1字节。


而我们现在最常用的是UTF-8标准,UTF-8标准兼容ASCII,在计算机中广泛使用!UTF-8最大的特点是根据不同范围的字符匹配使用不同的标准,因为ASCII 都是 0x??? 的形式,当识别到其他字符时,会匹配使用对应标准,比如当识别到汉字时,会使用 GBK 编码标准来进行输出(Windows)。

随着计算机的普及,越来越多的国家开始使用计算机,于是为了兼容几乎所有国家,出台了Unicode(中文又称万国码、国际码),提出了能适用更多语言的编码标准,即 UTF-16 和 UTF-32。,Unicode是计算机科学领域里的一项业界标准。它对世界上大部分的文字系统进行了整理、编码,使得电脑可以用更为简单的方式来呈现和处理文字。


basic_string类


说明

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

basic_string实例成员

  • string
    表示字符串的字符串类(本文主讲对象)。
  • wstring
    表示宽字符的字符串类,用来处理较长的字符串,在Windows下占2字节,Linux下占4字节。
  • u16string(C++11)
    匹配UTF-16标准,字符占位2字节。
  • u32string(C++11)
    匹配UTF-32标准,字符占位4字节。

    之所以有这么多basic_string字符串实例版本是为了适应不同国家语言字符串,而我们常用的是string类。

关于string

  1. string是表示字符串的字符串类
  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
  3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
  4. 不能操作多字节或者变长字符的序列。
  5. 在使用string类时,必须包含#include< string >头文件以及using namespace std(或using std::string)。

string类模块


构造函数

构造函数功能
string()默认构造函数
string (const string& str)拷贝构造函数
string (const string& str, size_t pos, size_t len = npos)从str中pos位置开始拷贝len个字符进行构造(默认将pos后的字符的全部拷贝,如果不足len个则也是全部拷贝)
string (const char* s)通过字符串s构造
string (const char* s, size_t n)从字符串s中拷贝n个字符构造
string (size_t n, char c)构造n个c字符组成的字符串
string (InputIterator first, InputIterator last)迭代器区间构造(迭代器介绍在后面)
//代码演示
#include <iostream>
using namespace std;
#include <string>

int main()
{
	const char* s = "ABCDEFGHIJKL";
	string s1; //默认构造
	string s2(s); //通过字符串s构造
	string s3(s2); //拷贝构造
	string s4(s3,2,5); //选择拷贝构造,从s3的2下标位置拷贝5个字符
	string s5(s, 5); //取s字符串中前五个字符构造
	string s6(10, 'a'); //10个字符a构造

	//迭代器区间构造(左闭右开区间)
	string s7(s, s + 12); //支持指针(指针也算是迭代器)
	string s8(s2.begin(), s2.end()); //通过容器迭代器构造(适用于所有支持迭代器的容器)

	return 0;
}

运行结果



空间大小相关

字符串长度及容量大小

  • size() 字符串大小
  • length() 字符串大小
    size与length几乎没区别,但是因为string是先于STL诞生的,后来为了统一于其他容器,增加了size,在使用时,主要以size为主!
  • capacity() 容量大小
    演示

清空字符串和空串查询

  • clear() 清空字符串(非释放对象)
  • empty() 查询字符串是否为空(空返回真)

字符串大小和容量设置

  • resize 设置字符串长度
    resize有两种调用方式,构成重载

    1.第一种是resize(size_t n) 将字符串设置为n,如果n小于当前字符串长度,则字符串长度缩小至n,如果大于n则字符串长度为n,增长的字符位初始化为0
    2.第二种是resize(size_t n,char c) 将字符串长度增长至n,如果n大于当前字符串长度,则增长的空间以字符c全部填充,如果低于字符串长度,则只修改字符串长度

    这里需要说明的是,如果resize的数量比当前容量大,则会进行扩容,但resize的数量比字符串小则只会抹除多余的字符,但并不会缩容,哪怕resize(0)也不会!
  • reserve(n) 设置容量为n
    我们可以手动设置字符串容量大小,以减小频繁扩容带来的性能损失! 虽然reserve可以设置容量,但是在VS平台下仍然不支持缩容,因为缩容的代价比扩容大,而且对于现代计算机来说这些少量空间没必要来回申请和释放!但如果我们在VS平台下reserve空间小于等于15时(在容量大于15的情况下),编译器会将前15个有效字符拷贝到缓冲区并释放申请的空间!在Linux平台下则是老老实实的缩容!

    这里要注意的是:
    VS的扩容策略是,VS预先有一个15字节大小的缓冲区,当我们的字符数量大于15时VS会开辟空间并通过指针管理,空间大小为30(第一次扩容为2倍),以后的每次扩容是1.5倍式增长,而且VS并不是稳定的按此算法去计算大小,每次都会多开一些空间。
    区别于VS,Linux下g++的string初识容量大小为0,扩容策略是第一次扩容为1,往后则是稳扎稳打的2倍式扩容,不会多开,而且可以缩容!


访问与遍历

头尾元素及字符串指针

  • front() 返回string中第一个字符
  • back() 返回string中最后一个字符
  • c_str() 返回字符串的指针类型
    string支持通过对象返回一个char类型指针指向字符串。

下标遍历

  • [ ] 下标访问
    string支持像访问数组一样访问字符串,越界会报错
  • at() 对象调用,下标访问
    功能与[ ]类似,但是at是通过对象调用且at下标越界时会抛异常

迭代器

迭代器成对出现,分别是 begin()指向第一个元素end()指向字符串末尾的下一个位置(即’’) ,所以迭代器是左闭又开区间;我们可以把迭代器想象成指针去使用,通过++/- -改变当前指向的元素位置,以及使用 " * " 解引用访问和修改,但迭代器本质不是指针

在使用迭代器时,不同容器有不同的迭代器且迭代器也分不同的类型,例如string的普通迭代器类型是 string::iterator 使用此类型定义一个迭代器并进行赋值!

获取容器迭代器只需要通过 类名::迭代器类型 即可定义迭代器!

  • 普通迭代器begin()end()
    普通的迭代器支持的使用与指针没有区别
    普通迭代器类型定义:string::iterator
  • 反向迭代器rbegin()rend()
    反向迭代器与普通迭代器不同点是,反向迭代器rbegin()指向字符串最后一个元素,rend()指向第一个元素的前一个位置(可以理解为-1),也是一个一闭一开区间,反向迭代器顾名思义,是倒着遍历的,++相当于普通迭代器的- -,其他的与普通迭代器相同
    反向迭代器类型定义: string::reverse_iterator
  • const迭代器cbegin()cend()
    const迭代器类型定义: string::const_iterator
  • const反向迭代器crbegin()crend()
    const反向迭代器类型定义: string::const_reverse_iterator

    const迭代器在原迭代器的基础上不能对迭代器所指向的元素进行修改,但是可以++/- -访问所有元素,在某些const传参的场景下会遇到

    虽然string有迭代器遍历,但是对于顺序性存储结构,大部分场景下还是使用 [ ] 下标遍历比较方便,所有支持迭代器的容器都支持范围for遍历元素


字符串插入与删除

字符串插入

  • push_back()
    push_back可以向字符串尾插一个字符
  • append()
    append的功能是在字符串尾部追加字符和字符串,而且有多种用法
    append有多个函数重载形式

    append的这些使用方法与前面构造函数的使用方式相似,有兴趣的小伙伴可以下去慢慢研究,不过最常用的是追加字符串!
  • operator += 运算符
    这个+=运算符的功能十分强大,是在尾部追加字符和字符串,在使用string时经常使用
  • operator = 运算符
    赋值运算符与平时使用一样,使用单个字符字符串直接覆盖原字符串!
  • insert
    insert支持任意位置插入字符串,插入依据既可以是下标也可以是迭代器
    insert也有许多重载形式

    不过,因为是任意位置插入,根据顺序表的性质,需要挪动元素,会有性能损失,一般按需使用!

删除字符串

  • erase
    erase支持任意位置删除,删除依据也是下标或迭代器
    erase也有三个重载版本
    –从pos位置开始删除len个字符(包括pos位置)
    –迭代器区间删除,删除first和last之间的字符串(左闭右开区间,删除范围从first开始,包括first,不包括last)


    这里要注意的是,与insert一样,顺序表删除元素是靠从后往前覆盖实现的,有一定的性能损失,一般情况下能少用就少用!

    关于npos
    这里要额外提一笔的是,npos在string中经常作为缺省参数出现,底层的机制是如果指定的操作长度大于字符串长度默认将此范围内一直到末尾的字符串全部进行操作,而npos是无符号整型-1,也就是整型的最大值,不过在不同平台,大小可能不同!


查找

  • find
    find有四种重载形式,也是查找字符串最常用的接函数> find只能使用下标进行查找,查找单个字符或字符串,在一个string中查找另一个string子串,返回的也是下标,如果没找到则返回npos(无符号整型的最大值),对于查找单个字符返回的是字符下标,查找字符串则是返回字符串首元素的下标!>
  • rfind
    rfind也有四个重载形式的函数,基本上与find保持一致,最大的特点是rfind是从后往前查找,反向遍历!
  • find_first_of
    这个函数比较特殊,查找字符串中出现的任意字符,默认pos位置是0(从头开始),可以指定起始查找位置,该函数有四种重载形式
  • find_last_of
    与find_first_of基本相似,只不过find_last_of是从后往前找
  • find_first_not_of
    与find_first_of类函数相反,在字符串中搜索与其参数中指定的任何字符不匹配的第一个字符
    该函数有四种重载形式
  • find_last_not_of
    与find_first_not_of相似,区别在于find_last_not_of是从后往前找


其他功能性函数

替换replace

replace
replace函数,可以替换字符串中一段,有多种重载形式


交换swap

swap
string内置了交换函数也支持库函数进行交换!


截取字符串substr

substr
substr支持从pos下标(默认为0)截取len个字符并重新构造一个string对象作为返回值


字符串比较函数compare

compare
string拥有一个比较函数compare,这个用的很少,因为没有运算符方便!



非成员函数

输入输出流

C++重载了operator <<>> 运算符,所以string类对象支持使用cin和cout进行流插入和流提取
这里需要注意的是cin流提取字符串时,是全覆盖式的写入字符串,cin输入字符串会覆盖以前的字符串!


获取一行字符串(包括空格)

getline函数支持一次截取一行字符串,碰到换行截至,这个函数在需要空格输入时非常实用!
其参数为cin和string对象


比较运算符重载

C++重载了字符串比较运算符,所以string拥有自己的比较规则


operator + 运算符

这个+运算符比较特殊,将两个字符串拼接以一个新的string对象作为返回值


最后

string的使用介绍到这里就介绍了,可以发现前辈们为了能自由自在摆弄字符串也是煞费苦心,好在字符串的操作在string的加持下确实非常方便,本文介绍了大部分接口,但是并非全部常用,想要掌握string还得平时多用并结合 string文档 加以升华!

本次 <C++ string使用> 就先介绍到这里啦,希望能够尽可能帮助到大家。

如果文章中有瑕疵,还请各位大佬细心点评和留言,我将立即修补错误,谢谢!
在这里插入图片描述

🌟其他文章阅读推荐🌟
C++ <模板> -CSDN博客
C++ <内存管理> -CSDN博客
C++ <类和对象 - 下> -CSDN博客
C++ <类和对象 - 中> -CSDN博客
C++ <类和对象 - 上> -CSDN博客
🌹欢迎读者多多浏览多多支持!🌹

​​

​​


风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。