【C++】string进一步介绍

zxctscl 2024-07-25 10:05:03 阅读 67

个人主页 : zxctscl

如有转载请先通知

文章目录

1. 前言2. 迭代器2.1 反向迭代器2.2 const对象迭代器

3. Capacity3.1 size和length3.2 max_size3.3 capacity3.4 clear3.5 shrink_to_fit (了解即可)3.6 reserve3.7 resize

4. Element access4.1 operator[]4.2 at

5. Modifiers5.1 push_back5.2 append5.3 operator+=5.4 assign(了解即可)5.5 insert5.6 erase5.7 replace5.8 swap

6. String operations6.1 c_str6.2 find 和 substr6.3 rfind6.4 compare

7. Non-member function overloads7.1 operator+7.2 getline

1. 前言

在之前的博客中初步介绍了string一下: 【C++】string类初步介绍,那么这次来看看它的实现。

2. 迭代器

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

函数名称 功能说明
operator[] (重点) 返回pos位置的字符,const string类对象调用
begin+ end begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
范围for C++11支持更简洁的范围for的新遍历方式
rbegin + rend rbegin反向迭代器以反向开始,end将迭代器返回到末尾

2.1 反向迭代器

在前面的一篇中已经提到了前面三个,这次来看看反向迭代器。

reverse_iterator:将给的字符串反向逆置。

在这里插入图片描述

<code>void test_string3()

{

string s1("hello,world");

string::reverse_iterator rit = s1.rbegin();

while (rit != s1.rend())

{

cout << *rit <<" ";

rit++;

}

cout << endl;

}

在这里插入图片描述

rbegin给rit,rit!=rend,然后加加rit。它本身就是反过来的,所以得用加加。

在这里插入图片描述

在这里插入图片描述

2.2 const对象迭代器

在这里插入图片描述

这里还有const对象的迭代器,他们两个有什么区别呢?

const要用const迭代器(const_iterator),不能修改对象值。

在这里插入图片描述

**iterator是可读可写,const_iterator只读。**会根据自己属性去调用。

<code>string::iterator it2 = s1.begin();

while (it2 != s1.end())

{

*it2 += 3;

cout << *it2 << " ";

it2++;

}

cout << endl;

const string s3("hello world");

string::const_iterator it3 = s3.begin();

while (it3 != s3.end())

{

cout << *it3 << " ";

it3++;

}

cout << endl;

在这里插入图片描述

在这里插入图片描述

rbegin同样有两种。

在这里插入图片描述

在这里插入图片描述

总共有四种迭代器:正向反向iterator,和正向反向const_iterator。用到最多是正向iterator。

3. Capacity

在这里插入图片描述

3.1 size和length

这里的size和length有什么区别呢?

直接用代码来测试一下:

<code>void test_string4()

{

string s1("hello world");

cout << s1.size() << endl;

cout << s1.length() << endl;

}

在这里插入图片描述

很显然,它们没有区别。有两个的原因是:string产生得比较早,没有出数据结构规范,在字符串长度取名字时候取的是length,后来stl出来之后,就增加了一个size。length是一个局限的取名,用size更统一。C++是两种都是兼容的。

3.2 max_size

在x86环境下来看看<code>max_size有多大:

在这里插入图片描述

但是不同平台的<code>max_size可能有所不同。

3.3 capacity

在这里插入图片描述

来看看capacity大小:

发现会比size要大

在这里插入图片描述

在这里插入图片描述

来看看string的扩容机制:

先取string当前的capacity,然后push_back,如果空间不够可能会引起capacity的变换;每次插入前获取新的capacity和旧的相比较看看相不相等,如果不相等,就把新的capacity赋值给旧的,并输出新的capacity。

<code> string s;

size_t sz = s.capacity();

cout << "making s grow:\n";

cout << sz << endl;

for (int i = 0; i < 100; ++i)

{

s.push_back('c');

if (sz != s.capacity())

{

sz = s.capacity();

cout << "capacity changed: " << sz << '\n';

}

}

第一次扩容的是原基础的2倍,剩下的都是1.5倍

在这里插入图片描述

在这里插入图片描述

与linux的扩容机制是不同的。

来看看linux的扩容机制:

在这里插入图片描述

g++扩容是两倍扩:

在这里插入图片描述

STL是一个规范,规定功能,没有规定实现细节。

3.4 clear

在这里插入图片描述

clear是指的清数据,空间不一定清理:

<code>string s1("hello world");

cout << s1.size() << endl;

cout << s1.length() << endl;

cout << s1.capacity() << endl;

cout << s1 << endl;

cout << s1.capacity() << endl;

s1.clear();

cout << s1 << endl;

cout << s1.capacity() << endl;

capacity并没有改变。

在这里插入图片描述

就算将s1多输入字符,它的的capacity在clear后也不会改变;

在这里插入图片描述

3.5 shrink_to_fit (了解即可)

如果想要缩容用就要用shrink_to_fit :

在这里插入图片描述

<code>string s1("hello worldxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

cout << s1.size() << endl;

cout << s1.length() << endl;

cout << s1.capacity() << endl;

cout << s1 << endl;

cout << s1.capacity() << endl;

cout << s1.size() << endl;

s1.clear();

cout << s1 << endl;

cout << s1.capacity() << endl;

cout << s1.size() << endl;

s1.shrink_to_fit();

cout << s1 << endl;

cout << s1.capacity() << endl;

cout << s1.size() << endl;

这里缩容到15

在这里插入图片描述

3.6 reserve

注意区分:

reserve是保留

reverse是反转,翻转

reserve是用来扩容的。

在这里插入图片描述

<code>string s;

s.reserve(100);

size_t sz = s.capacity();

cout << "making s grow:\n";

cout << sz << endl;

for (int i = 0; i < 100; ++i)

{

s.push_back('c');

if (sz != s.capacity())

{

sz = s.capacity();

cout << "capacity changed: " << sz << '\n';

}

}

在这里插入图片描述

在linux里面:

在这里插入图片描述

在这里插入图片描述

reserve会不会缩容呢?

来看看代码:

<code>string s1("hello worldxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

cout << s1.size() << endl;

cout << s1.capacity() << endl;

s1.reserve(20);

cout << s1.size() << endl;

cout << s1.capacity() << endl;

reserve是不会缩容的。

reserve比capacity大才扩容。

在这里插入图片描述

3.7 resize

resize改变size。

在这里插入图片描述

resize有三种情况。

resize从三个角度来对它进行分析:

假设这里size是17,capacity是32

在这里插入图片描述

resize给的比size小,会删除

<code>string s2("hello worldxxxx");

cout << s2.size() << endl;

cout << s2.capacity() << endl;

s2.resize(10);

cout << s2.size() << endl;

cout << s2.capacity() << endl;

在这里插入图片描述

resize给的在size和capacity之间,插入

<code> string s2("hello worldxxxx");

cout << s2.size() << endl;

cout << s2.capacity() << endl << endl;

/*s2.resize(10);*/

s2.resize(20);

cout << s2.size() << endl;

cout << s2.capacity() << endl;

默认插入\0

在这里插入图片描述

resize给的比capacity大,扩容+插入

在这里插入图片描述

在这里插入图片描述

总之:

在这里插入图片描述

所以知道要插入多少数据,就提前开好空间,避免了扩容,提高了效率。

4. Element access

在这里插入图片描述

4.1 operator[]

在上一篇博客中已经提过了,有需要可以看看【C++】string类初步介绍

在这里插入图片描述

在用[]越界是断言错误:

在这里插入图片描述

4.2 at

在这里插入图片描述

<code> string s1("hello world");

cout << s1[6] << endl;

cout << s1.at(6)<< endl;

在这里插入图片描述

at与[]的越界报错不一样

用at越界时候报的是非法

在这里插入图片描述

5. Modifiers

5.1 push_back

尾插一个字符

在这里插入图片描述

想尾插一个字符:

<code>void test_string7()

{

string s1("hello world");

s1.push_back('!');

cout << s1 << endl;

}

在这里插入图片描述

5.2 append

append尾插,可以插入一个字符,也可以插入字符串。

在这里插入图片描述

<code> string s1("hello world");

/*s1.push_back('!');*/

s1.append("!");

cout << s1 << endl;

s1.append("abcd");

cout << s1 << endl;

在这里插入图片描述

在这里插入图片描述

一般用得最多的就是:

在这里插入图片描述

5.3 operator+=

+=用起来就比较方便

来看个例子:

<code> string s1("hello world");

s1 += ' ';

s1 += "abc";

cout << s1 << endl;

在这里插入图片描述

5.4 assign(了解即可)

assign赋值,字符覆盖

在这里插入图片描述

可以把当前字符覆盖:

<code>string s1("hello world");

cout << s1 << endl;

s1.assign("xxxxx");

cout << s1 << endl;

在这里插入图片描述

5.5 insert

insert都是在当前位置的前面插入

在这里插入图片描述

常用的就是:

在这里插入图片描述

举个例子:

<code>string s1("hello world");

cout << s1 << endl;

s1.insert(0, "abc");

cout << s1 << endl;

在这里插入图片描述

5.6 erase

erase删除

在这里插入图片描述

常用:

在这里插入图片描述

如果这个内容太短小于npos,就全部删除。

举个例子:

<code>string s1("hello world");

cout << s1 << endl;

/*s1.insert(0, "abc");*/

s1.erase(5,10);

cout << s1 << endl;

在这里插入图片描述

erase不给值就直接删空了:

在这里插入图片描述

5.7 replace

replace替换

在这里插入图片描述

把pos位置,一个字符替换成两个xx:

<code> string s2("hello world");

cout << s2 << endl;

s2.replace(6, 1, "xx");

cout << s2 << endl;

在这里插入图片描述

一般结合find()使用:

在这里插入图片描述

在这里插入图片描述

来看个代码:

<code>string s2("hello world hello abcd");

size_t pos = s2.find(' ');

while (pos != string::npos)

{

s2.replace(pos, 1, "%20");

pos = s2.find(' ');

}

cout << s2 << endl;

在这里插入图片描述

insert erase replace要少用,因为基本上都要挪动数据,效率不高。

像替换这里还可以用范围for:

<code>string s3;

for (auto ch : s2)

{

if (ch != ' ')

{

s3 += ch;

}

else

{

s3 += "%20";

}

}

cout << s3 << endl;

在这里插入图片描述

5.8 swap

在这里插入图片描述

来看一个例子:把空格位置换成20%:

<code>void test_string9()

{

string s2("hello world hello abcd");

string s3;

s3.reserve(s2.size());

for (auto ch : s2)

{

if (ch != ' ')

{

s3 += ch;

}

else

{

s3 += "20%";

}

}

cout << s3 << endl;

s2.swap(s3);

cout << s2 << endl;

}

在这里插入图片描述

6. String operations

在这里插入图片描述

6.1 c_str

在C语言中有打开文件的操作,在c++里面要打开文件就要用到c_str,让它来兼容C语言。

在这里插入图片描述

来个例子:

<code>void test_string10()

{

string s1("hello world");

string filename("test.cpp");

FILE* fout = fopen(filename.c_str(), "r");

}

6.2 find 和 substr

find查找

在这里插入图片描述

substr去一个字符串的字串。

在这里插入图片描述

如果想要拿到一个文件的后缀,就用find,但要将后缀拷贝下来就得用到substr。

来看看简单的实现:

<code>void test_string10()

{

string s1("file.cpp");

size_t pos1= s1.find('.');

if (pos1 != string::npos)

{

string suffix = s1.substr(pos1);

cout << suffix << endl;

}

else

{

cout << "没有后缀" << endl;

}

}

在这里插入图片描述

在这里插入图片描述

6.3 rfind

如果查最后一个序列怎么办呢?

用rfind,从后往前找

在这里插入图片描述

<code>void test_string10()

{

string s1("file.cpp.tar.zip");

size_t pos1= s1.rfind('.');

if (pos1 != string::npos)

{

string suffix = s1.substr(pos1);

cout << suffix << endl;

}

else

{

cout << "没有后缀" << endl;

}

}

在这里插入图片描述

在这里插入图片描述

如果给的网站很多怎么按协议,域名,网址分开呢?

在这里插入图片描述

协议到:,域名从i+3的位置开始,到第一个/就结束。网址就是剩下的部分

<code>void test_string10()

{

string url1("https://legacy.cplusplus.com/reference/string/string/substr/");

string protocol, domain, uri;//协议,域名,网址

size_t i1 = url1.find(':');

if (i1 != string::npos)

{

protocol = url1.substr(0, i1 - 0);

cout << protocol << endl;

}

size_t i2 = url1.find('/',i1+3);

if (i2 != string::npos)

{

domain = url1.substr(i1+3, i2-(i1+3));

cout << domain << endl;

uri = url1.substr(i2+1);

cout << uri << endl;

}

}

在这里插入图片描述

6.4 compare

compare是按照ascii比较

在这里插入图片描述

<code> string str1("green apple");

string str2("red apple");

cout << (str1 < str2) << endl;

r的ASCII比g的ASCII小:

在这里插入图片描述

7. Non-member function overloads

在这里插入图片描述

7.1 operator+

在这里插入图片描述

来用代码实现一下:

<code> string ss1 = "xxx";

string ss2 = "yyy";

string ret = ss1 + ss2;

cout << ret << endl;

在这里插入图片描述

还支持这样的写法:

<code> string ret1 = ss1 + "yyy";

string ret2 = "yyy"+ss2;

cout << ret1 << endl;

cout << ret2 << endl;

在这里插入图片描述

在这里插入图片描述

7.2 getline

getline获取一行。

在这里插入图片描述

举个例子:获得一个字符串里面最后一个单词的长度

<code>#include<iostream>

#include<string>

using namespace std;

int main()

{

string line;

// 不要使用cin>>line,因为会它遇到空格就结束了

// while(cin>>line)

while (getline(cin, line))

{

size_t pos = line.rfind(' ');

cout << line.size() - pos - 1 << endl;

}

return 0;

}

有问题请指出,大家一起进步!!!



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。