【C++】string的使用


请添加图片描述

前言

  上一节我们简单介绍了STL是什么以及对string字符串类做了个开头,那这节我们就来继续了解string类的使用。大家也可以对照着这个链接来查看string类的各种接口和使用说明。
string-C++Reference

本文只介绍string中的比较常见重要的接口,不会每一个都细说。

string类对象的常见构造

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

他们的调用实现也很简单:

//常用
string s1;  //无参构造
string s2("hello world");//用c格式字符串构造string类对象s2
string s3(s2);//拷贝构造

string s4(10, 'x');//用10个字符c类型构造s4对象

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

结果如下:
在这里插入图片描述

  当调用无参构造函数时,系统默认将对象初始化为空串,即只包含’\0’的字符串。所以会发现第一行是空出来的。

string类对象的容量操作

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

1.size、length和capacity

  我们用sizecapacity获取字符串的有效长度对象的容量大小

注意:字符串的有效长度是不包括’\0’的。

string s("hello world!!");
cout << "The string is " << s << endl;
cout << "The string size is " << s.size() << endl;
cout << "The string lengrh is " << s.length() << endl;
cout << "The string capacity is " << s.capacity() << endl;

结果如下:
在这里插入图片描述
  我们发现size和length的结果是一样的,那他们究竟有什么区别呢:

  1. string诞生与STL之前,最开始设计长度就是length()。
  2. 当string并入到STL之后,STL中其他容器获取大小都使用size(),为了统一,就也给string类加上了size()。
  3. 为了统一,推荐大家使用size()。

2.empty和clear

  empty判断字符串是否为空串clear清空有效字符

注意:清空有效字符只是将size清0,不改变底层的capacity的大小。

//接上段代码
cout << s.empty() << endl;
s.clear();//清空字符串的有效长度
cout << "The string size is " << s.size() << endl;
cout << "The string capacity is " << s.capacity() << endl;

结果如下:
在这里插入图片描述
  第一行0则表示字符串不为空。
  使用了clear后发现size已经变成了0,但是capacity不变。

3.reserve

  reserve是为字符串预留空间,说明白话就是扩容,知道我们需要多少空间可以提前开好。

注意:它只影响capacity,不会影响size。

如下代码:

string s;
s.reserve(100);
s[100] = 'x';

  结果就会崩溃,因为size还是0:


  那reserve可以扩容,那它是否可以缩容呢,答案是可以,但不建议。
看下面代码:

string s;
s.reserve(100);
cout << "The string size is " << s.size() << endl;
cout << "The string capacity is " << s.capacity() << endl;
//想缩容,把capacity缩至50.
s.reserve(50);
cout << "The string size is " << s.size() << endl;
cout << "The string capacity is " << s.capacity() << endl;
//想继续缩容,把capacity缩至5.
s.reserve(5);
cout << "The string size is " << s.size() << endl;
cout << "The string capacity is " << s.capacity() << endl;

结果如下:
在这里插入图片描述
  我们发现缩容至50时,它的capacity并没有发生改变,但是缩容至5时,它的capacity又发生了改变。
  先说结论:在 VS 下,若reserve给的值小于当前 capacity ,同时大于16,默认不缩容。

具体原因,大家可以参考这篇博客,有详细的解答:
【C++ 第五章】string 类(补充):string 扩展空间规律 + reserve 的缩容 + resize 的使用

  但是在Linux下,编译器则会遵循reserve,根据reserve改变实时改变capacity。
  所以总结来说:虽然reserve有缩容功能,但是一般不使用缩容,因为不同的平台reserve缩容规则不同,会导致代码的可迁移性降低。

4.resize

  resize将有效字符的个数改为n个,多出的空间用字符c填充。它也可以算是扩容,但是它与reserve的区别是reserve只影响capacity,而resize既影响size又影响capacity。
在这里插入图片描述

  这两种都是将字符串的有效个数变成n个,不同的是当字符个数增多时,resize(size_t n)是用’\0’来填充多出的元素空间,resize(size_t n, char c)是用字符c来填充多出的元素空间。
在这里插入图片描述


  若原空间中有字符串时,resize时不会改变原来的字符串,只会影响剩余未初始化的空间。
在这里插入图片描述

注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变capacity的大小,但是如果是将元素个数减少,则底层空间总大小即capacity不变。

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

函数名称功能说明
operator[](重点)返回pos位置的字符
begin + endbegin获取一个字符的迭代器+end获取最后一个字符下一个位置的迭代器
rbegin + rendrbegin获取最后一个字符+rend获取第一个字符的前一个字符

1.operator[]

  我们想要遍历这个字符串时,可以使用下标+[]的形式,像数组一样去遍历
  operator[]在cplusplus中定义了两种形式:
在这里插入图片描述

:两种形式都采取了传引用返回,它可以减少拷贝,且可以修改返回对象。

string s("hello!");
size_t pos = 0;//下标
for (pos = 0; pos < s.size(); pos++)
{
	//可以像数组一样直接用下标访问每个字符
	cout << "第" << pos + 1 << "个字符是" << s[pos] << endl;
}

在这里插入图片描述


可以修改返回对象:

string s("hello!");
size_t pos = 0;//下标
for (pos = 0; pos < s.size(); pos++)
{
	s[pos]++;
}
for (pos = 0; pos < s.size(); pos++)
{
	cout << s[pos] << " ";
}
cout << endl;

结果则是:i f m m p "


  在使用const形式时,要注意它的字符不能被修改的。

const string s1("haha");
s1[1] = 'x';

会报错:error C3892: “s1”: 不能给常量赋值

补充operator[]在库里面重载定义时已经包含了检查,可以防止越界。

2.迭代器

  在访问和遍历string类对象时,可以使用下标+[]访问,但是这种形式不适合所有的底层结构,如链表,树等就不适合,此时就有一种新的形式出现:迭代器。
  迭代器:我们暂时理解为这是一个像指针一样的东西类似指针),不能说它就是指针。
  对于迭代器的使用如下:

string s2("haha");
//创建迭代器,要将迭代器定义在类域里面
string::iterator it1 = s2.begin();
//begin获取一个字符的迭代器,相当于it1是指向第一个字符的指针,但迭代器并不是指针。
while (it1 != s2.end())//end获取最后一个字符下一个位置的迭代器
{
	cout << *it1 << " ";
	it1++;
}

在这里插入图片描述
  同样它还存在反向迭代器(倒过来遍历)

string s2("haha");
//rbegin获取最后一个字符的迭代器,相当于rit1是指向最后一个字符的指针,但迭代器并不是指针。
string::reverse_iterator rit1 = s2.rbegin();
while (rit1 != s2.rend())
{
	cout << *rit1 << " ";
	rit1++;
}

在这里插入图片描述

:迭代器的遍历区间都是左闭右开。

当然还存在只读的迭代器,用来遍历只读字符串。

  • const_iterator正向遍历常量字符串
  • const_reverse_iterator反向遍历常量字符串
string s2("haha");
//创建迭代器,要将迭代器定义在类域里面
string::const_iterator it1 = s2.begin();
//等价于auto it1 = s2.begin();
while (it1 != s2.end())//end获取最后一个字符下一个位置的迭代器
{
	//*it1++;   错误,不能修改
	cout << *it1 << " ";
	it1++;
}


const_iterator 是迭代器指向的数据不能修改
const iterator是迭代器本身不能修改

我们前面说到范围for,其实它的底层结构就是迭代器

for(auto& e : s2)
{
	cout<<e<<" ";
}

范围for的特点就是自动取值,自动加加,自动结束。

string类对象的修改操作

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

1.push_back、append和operator+=

push_back:尾插字符。
append:尾插字符/字符串
operator+=:尾插字符/字符串

string s1("hello");
s1.push_back('!');
cout << "s1:" << s1 << endl;

string s2("hello ");
s2.append("world ");
cout << "s2:" << s2 << endl;

string s3("hello ");
s3 += "world";
cout << "s3:" << s3 << endl;

在这里插入图片描述
  我们发现append和operator+=的结果是一样的,但是operator+=使用起来方便很多,所以我们推荐使用operator+=。

2.c_str

  有时候需要获取使用到指向字符串的指针,而这又是c语言中的内容,C++兼容C,所以string中就提供了c_str这个接口来帮助实现。

string s4("hello");
cout << s4.c_str() <<  endl;

在这里插入图片描述
  在c语言中,当指针指向常量字符串时,编译器会直接打印所有内容。

3.insert和erase

  insert是在任意位置插入字符/字符串

string s5("0123456");
s5.insert(2, "abc");//在第2个位置插入字符串"abc"
s5.insert(4, 1, 'd');//在第4个位置插入一个字符d
cout << s5 << endl;

在这里插入图片描述

要注意的是:当我们想要头插一个字符时,
必须要写成:s.insert(0,1,'y');
而不能直接写成s.insert(0,‘y’),把1省略掉了这是不行的。
当然也可以使用迭代器来实现头插一个字符:s.insert(s.begin(),'y');


erase是在任意位置删除字符/字符串

string s6("123456");
s6.erase(3, 2);//从第3个位置开始删除两个字符
cout << "s6.erase(3, 2):" << s6 << endl;
s6.erase();//无参则代表全删
cout << "s6.erase():" << s6 << endl;

在这里插入图片描述
  无参则代表全删是因为erase()是一个全缺省参数pos的缺省值为0,代表从0位置开始删,长度len的缺省值为npos,这代表无符号整型中的-1,则为整数最大值,肯定大于字符长度,就等于全部删完。
在这里插入图片描述
在这里插入图片描述

注意:insert和erase的时间复杂度都是O(n),效率不高,都要慎用。

4. find+npos和substr

  find找到字符在字符串中的位置
  substr从pos位置开始截取n个字符并返回。

例:我们想要对一个网址分成三段,一段是协议,一段是域名,最后一段是路径。

//  https://legacy.cplusplus.com/reference/string/string/substr/
//想将这个链接分成三段

string s("https://legacy.cplusplus.com/reference/string/string/substr/");
cout << "链接为:" << s << endl;
size_t pos1 = s.find(':');//找到冒号,并返回它所在的位置
string s1 = s.substr(0, pos1 - 0);//从下标0开始,想要截取pos1-0个长度的字符串
//左闭右开区间下标相减就是长度
cout << "协议为:" << s1 << endl;

size_t pos2 = s.find('/', pos1 + 3);//从pos1+3的位置即字符c这个位置开始往后找'/'
string s2 = s.substr(pos1 + 3, pos2 - (pos1 + 3));
cout << "域名为:" << s2 << endl;

string s3 = s.substr(pos2 + 1);//从pos2+1开始直到最后
//只有一个参数,截取长度为缺省值npos,为最大整数,因此直接取到最后
cout << "路径为:" << s3 << endl;

在这里插入图片描述

string类非成员函数

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

1. operator+

  operator+和operator+=一样,同样可以连接字符/字符串,但是operator+是传值返回,深拷贝效率低下。

string s1 = "hello ";
string s2 = "world";
string ret1 = s1 + s2;
string ret2 = "hehe " + ret1;
cout << "ret1:" << ret1 << endl;
cout << "ret2:" << ret2 << endl;

在这里插入图片描述
  由于编译器想直接像ret2一样直接字符串+string,这样就必须重载成全局函数,所以这是string类的非成员函数。

2. operator>>和operator<<

  我们可以像内置类型一样对string类直接使用流提取和流插入是因为对这两个函数进行了重载

string s;
cin >> s;
cout << s << endl;

在这里插入图片描述

3.getline

  由于cin在输入时遇到空格或换行会结束,因此我们想输入获取一行包含空格的字符串时,就可以用到getline。
在这里插入图片描述
getline的使用:

string s1;
getline(cin, s1);
cout << s1 << endl;

在这里插入图片描述

getline默认换行表示结束。
在使用时需要加上#include< string>头文件

4.relational operators

  relational operators也就是按字符的ASCLL码(字典序)进行比较。
在这里插入图片描述

string s2("haha");
string s3("hehe");
cout << (s2 < s3) << endl;

在这里插入图片描述


感谢大家观看,如果大家喜欢,希望大家一键三连支持一下,如有表述不正确,也欢迎大家批评指正。
请添加图片描述

  • 16
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值