string的使用
- 1. STL
- 2. string
- 2.1 初始化和遍历
- 2.2 容量相关
- 2.3 串的修改
- 2.4 其他接口
1. STL
STL全称 standard template libaray——标准模板库,内部包含了很多数据结构和算法,数据结构包括栈,队列,树,链表等,算法包括交换,排序,查找,逆置等。是一个C++标准库的重要组成部分。STL不止含有这两部分,接下来就慢慢进入STL的学习,先从使用开始。
这里有一个网址,大家可以在这个网址上学习
https://legacy.cplusplus.com/
2. string
string就是字符串,它其实是类模板实例化出来的一个类,存储的是字符
2.1 初始化和遍历
初始化是通过构造函数进行的,先来看看有哪些吧
第一个默认构造;第二个拷贝构造,通过一个已存在的对象初始化;第三个通过存在对象的部分内容初始化;第四,五个都是通过字符串来初始化;第六个是通过n个字符初始化;最后一个是通过迭代器区间初始化。用的比较多的是第2,3,4,5个。
在string和vector(顺序表)中迭代器的使用和指针基本类似。后续会介绍迭代器具体是什么。
遍历方式1:下标 + [ ]
string类重载了operator[ ],可以通过类似数组那样的方式遍历string对象。
遍历方式2:范围for循环遍历
遍历方式3:迭代器遍历
迭代器类似指针
begin可以获得string第一个字符的地址,end可以获得string最后一个字符的下一个位置的地址。
rbegin可以获得string反向的第一个字符的地址,即最后一个字符的地址;rend可以获得string的反向的最后一个字符的下一个位置的地址。如下图
最后四个是对const对象新增的函数,但是前四个已经重载了const版本的,所以后四个不需要使用。
并且每一种容器都有迭代器,因此使用时,需要指定类域。返回值类型大家可以点进去自行查看。
对于这三种遍历,给出如下实例代码。
#include<iostream>
#include<string>
using namespace std;
void test2()
{
//使用字符串来初始化,for循环+下标+ [ ]遍历
string s1("ABCDEFG");
//for循环
for (int i = 0; i < s1.size(); i++)
{
cout << s1[i];
}
cout << endl;
//拷贝构造初始化,范围for遍历
string s2(s1);
for (auto ch : s2)
{
cout << ch;
}
cout << endl;
//n个字符初始化,迭代器遍历
string s3(10, 'B');
string::iterator i = s3.begin();
while (i != s3.end())
{
cout << *i;
i++;
}
cout << endl;
//反向遍历
const string s4 = s1;
string::const_reverse_iterator ci = s4.rbegin();
while (ci != s4.rend())
{
cout << *ci;
ci++;
}
cout << endl;
}
int main()
{
test2();
return 0;
}
运行结果如下图
总结:推荐使用下标 + [ ] ,可以正向遍历,也可以逆向遍历;范围for不支持逆向遍历。迭代器可读性较差。对于后续的无法使用下标 + [ ] 遍历的,就要使用迭代器遍历了。
2.2 容量相关
size和length都是获得字符串的大小;
string s(“ABCDEFG”);
cout<<s.size()<<endl;
max_size是获得可初始化的最长字符串的长度(没有什么作用);
resize是更新string对象size的大小。
capacity是获得string对象capacity的大小,vs上默认是16
#include<iostream>
#include<string>
using namespace std;
void test3()
{
string s("ABCDEFG");
cout <<"更新前size = "<< s.size() << endl;
cout <<"更新前capacity = "<< s.capacity() << endl;
s.resize(20);
cout << "更新后size = " << s.size() << endl;
cout << "更新后capacity = " << s.capacity() << endl;
}
int main()
{
test3();
return 0;
}
运行结果如下
当更新的size超过15时,会更改capacity的大小,原因是空间不够需要扩容。
下面这段代码可以观察vs是如何扩容的。
void test_capacity()
{
string s;
//保留原大小
size_t capacity = s.capacity();
cout << "capacity:" << s.capacity() << endl;
//循环插入字符A,空间不够会扩容
for (int i = 0; i < 200; i++)
{
s += 'A';
//扩容时打印
if (capacity != s.capacity())
{
cout << s.capacity() << endl;
capacity = s.capacity();
}
}
}
运行结果如下
起始值是15,第一次扩容为2倍,后续都是1.5倍。扩容的消耗很大,因此如果事先能知道需要多少空间,可以一次开完全就不会造成这样的消耗了。
reserve可以扩充容量,用法如下
string s;
s.reserve(扩容后的大小)
还是上面的代码,如果实现就给好200个空间。那么就不需要扩容了。
运行结果可以看出,没有扩容,但是容量不是200,它可能多给空间,但不会少给。
但是给的数值如果小于原来的空间,这个函数就不做处理。
总结:resize会改变size,可能改变capacity;reserve会改变capacity,不会改变size
clear可以将字符串内容清空,使之变成空串,改变size(变为0),不改变capacity
empty判断是否是空串。
shrink_to_fit可以缩小串的容量(不建议使用,因此不做详谈)
2.3 串的修改
**operator+=**是重载的运算符,可以尾插一个字符串或字符或string对象
push_back尾插一个字符
append用法较多,但不如operator+=方便,可读性也不如operator+=,因此不做过多介绍。
insert用法也比较多,这里介绍和演示几个较为常用的。
第一个:在pos位置插入一个string对象;第三个:在pos位置插入一个字符串;第四个:在pos位置插入字符串的n个字符;第五个:在pos位置插入n个字符
assign是给该字符串一个新的值,替换当前的内容,用法和初始化很类似。
erase为删除字符
第一个:删除pos位置的len个字符,不给实参或者len超过字符串的长度时,将全部删除。npos为整型的最大值。
第二个:删除一个迭代器位置的字符
第三个:删除一段迭代器区间
swap为交换两个string对象
pop_back为删除最后一个字符
2.4 其他接口
c_str和data可以获得c类型的字符串。如下图
这两个函数获得的是**_str**,因为有些C语言的接口操作的是字符串,不是string对象。
find和rfind分别是正向和反向找第一个符合条件的字符或字串。找到返回下标,找不到返回npos
比如我们要打印这句英文的第一个和最后一个单词Life is full of possibilities可以这样写。
void test_string2()
{
string s = "Life is full of possibilities";
size_t pos = s.find(' '); //拿到第一个空格的下标,即4
string sbegin = s.substr(0,pos);//拿到字串,从下标为0到下标为pos位置之前,左闭右开 [0 ,pos)
cout << sbegin << endl;
size_t pos1 = s.rfind(" ");//拿到最后一个空格的下标
string send = s.substr(pos1 + 1);//拿到字串,从pos1+1开始,没有右区间,就会取完全部字符
cout << send << endl;
}
运行结果如下
substr为获取字串,上面已经有介绍用法了,这里看下函数原型,就不多解释了。
compar为比较两个串的大小,原理和strcmp一样。
其他函数用的很少,大家可以自己对照文档了解。关于string常用函数接口的解释就到这里了。