STL(standard template libaray-标准模板库):是C++标准库的重要组成部分 ,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。
C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问 。
在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数 。
- string是表示字符串的字符串类
- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作
- string在底层实际是:basic_string模板类的别名,typedef basic_string
string; - 不能操作多字节或者变长字符的序列。
在使用string类时,必须包含#include头文件以及using namespace std;
构造函数名称 | 功能说明 |
---|---|
string()(重点) | 构造空的string类对象,即空字符串 |
string(const char* s) (重点) | 用C-string来构造string类对象 |
string(size_t n, char c) | string类对象中包含n个字符c |
string(const string&s) (重点) | 拷贝构造函数 |
函数名称 | 功能说明 |
---|---|
size(重点) | 构造空的string类对象,即空字符串 |
length | 用C-string来构造string类对象 |
capacity | string类对象中包含n个字符c |
empty(重点) | 拷贝构造函数 |
clear(重点) | 清空有效字符 |
reserve(重点) | 为字符串预留空间 |
resize(重点) | 将有效字符的个数改成n个,多出的空间用字符c填充 |
void TestString1()
{
string s("hello");
cout << s.size() << endl; //返回字符串中的有效字符,不包含'\0'
cout << s.length() << endl; //等同于size,但是通常不用length求有效字符
cout << s.capacity() << endl; //返回空间总大小,同样不包含'\0'
cout << s << endl; //string类对象支持cin输入和cout打印
// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小
s.clear();
cout << s.size() << endl;
cout << s.capacity() << endl;
// 将s中有效字符个数增加到10个,多出位置用'a'进行填充
// “aaaaaaaaaa”
s.resize(10, 'a');
cout << s.size() << endl;
cout << s.capacity() << endl;
// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充
// "aaaaaaaaaa\0\0\0\0\0"
// 注意此时s中有效字符个数已经增加到15个
s.resize(15);
cout << s.size() << endl;
cout << s.capacity() << endl;
cout << s << endl;
// 将s中有效字符个数缩小到5个
s.resize(5);
cout << s.size() << endl;
cout << s.capacity() << endl;
cout << s << endl;
}
假设不指定空间大小则每次都会扩容,带来计算开销
// 利用reserve提高插入数据的效率,避免增容带来的开销
void TestPushBack()
{
string s;
size_t sz = s.capacity();
cout << s.capacity() << endl;
cout << "making s grow:\n";
for (int i = 0; i < 100; ++i)
{
s.push_back('c');
//查看扩容了几次
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
reserve的应用场景是为了解决string的增容缺陷,假设我们已经知道要开辟多大空间时,可以直接指定空间大小,避免增容带来的开销
// 利用reserve提高插入数据的效率,避免增容带来的开销
void TestPushBackReserve()
{
string s;
s.reserve(100);
size_t sz = s.capacity();
cout << s.capacity() << endl;
cout << "making s grow:\n";
for (int i = 0; i < 100; ++i)
{
s.push_back('c');
//查看扩容了几次
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
使用reserve注意以下问题
void TestString3()
{
string s;
// 测试reserve是否会改变string中有效元素个数
s.reserve(100);
cout << s.size() << endl;
cout << s.capacity() << endl;
// 测试reserve参数小于string的底层空间大小时,是否会将空间缩小
s.reserve(50);
cout << s.size() << endl;
cout << s.capacity() << endl;
}
size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
clear()只是将string中有效字符清空,不改变底层空间大小。
resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字
符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的
元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大
小,如果是将元素个数减少,底层空间总大小不变。
reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于
string的底层空间总大小时,reserver不会改变容量大小。
reserve和resize的区别是,reserve只增容不改变有效元素个数,resize即改变容量空间又对容量空间初始化。
函数名称 | 功能说明 |
---|---|
operator[] | 返回pos位置的字符,const string类对象调用 |
begin+ end | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
rbegin + rend | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭 代器 |
范围for | C++11支持更简洁的范围for的新遍历方式 |
void TestStringOperator1()
{
string s1("hello world");
cout << s1 << endl;
s1[0] = 'H';
cout << s1 << endl;
const string s2("hello c++");
// s2[0] = 'h'; 代码编译失败,因为const类型对象不能修改
}
对于迭代器,我们暂时可以将其看成指针
//迭代器
void TestStringIterator()
{
string s("hello world");
//三种遍历方式:
//以下三种方式除遍历string外还可修改string中的字符
//第一种使用较多
//1.for+operator[]
cout << "for+operator[]" << endl;
for (size_t i = 0; i < s.size() - 1; ++i)
{
cout << s[i] << " "<< endl;
}
//2.正向迭代器
cout << "2.正向迭代器" << endl;
string::iterator it = s.begin();
while (it != s.end())
{
cout << *it << endl;
++it;
}
//2.反向迭代器
cout << "2.反向迭代器"<<endl;
string::reverse_iterator rit = s.rbegin();
while (rit != s.rend())
{
cout << *rit << endl;
++rit;
}
//3.范围for
cout << "3.范围for" << endl;
for (auto ch : s)
{
cout << ch << endl;
}
函数名称 | 功能说明 |
---|---|
push_back | 在字符串后尾插字符c |
append | 在字符串后追加一个字符串 |
operator+= (重点) | 在字符串后追加字符串str |
c_str(重点) | 返回C格式字符串 |
find + npos(重点) | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
//找文件尾
void TestStringFind()
{
string s = "hello world";
s.push_back('!'); //字符串后尾插字符
cout << s << endl;
s.append("HELLO"); //字符串后追加字符串
cout << s << endl;
s += 'W'; //+=追加字符
cout << s << endl;
s += "ORLD"; //+=追加字符串
cout << s << endl;
s += s; //追加string对象
cout << s << endl;
string file1("string.cpp");
string file4("string.c.tar.zip");
//substr第二个参数可以不写,从前往后找
size_t pos = file1.find('.');
if (pos != string::npos)
{
cout << file1.substr(pos, file1.size() - pos) << endl;
}
//rfind从后往前找
size_t pos4 = file4.rfind('.');
if (pos4 != string::npos)
{
cout << file4.substr(pos4) << endl;
}
//分割域名
string url = "http://cplusplus.com/reference/string/string/find/";
cout << url << endl;
size_t pos5 = url.find(':'); //第一个冒号分割http
if (pos5 != string::npos)
{
cout << url.substr(0, pos5) << endl;
}
size_t pos6 = url.find('/', pos5 + 3); //pos5+3:c pos6:/
if (pos6 != string::npos) //分割网址cplusplus.com
{
cout << url.substr(pos5 + 3, pos6 - (pos5 + 3)) << endl;
}
cout << url.substr(pos6 + 1, string::npos) << endl;
//删除http
string copy = url;
cout << copy.erase(0, pos5 + 3) << endl;
}
注意:
函数 | 功能说明 |
---|---|
operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
operator>> (重点) | 输入运算符重载 |
operator<< (重点) | 输出运算符重载 |
getline (重点) | 获取一行字符串 |
relational operators (重点) | 大小比较 |