STL与string类的认识及简单使用
- 一、STL
- 二、string类
- 构造函数
- 容量操作
- 访问及遍历操作
- 迭代器
- 修改操作
- 非成员函数重载
- 关系运算符重载
- getline
- 三、总结
一、STL
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。
这里仅仅是简单介绍一下STL六大组件,后面结合实例细细分析
二、string类
【使用文档】
string从功能上来说属于STL容器,但是从发展历史上来讲,并不是。本文我们只讲述string(utf-8):编码以一个字节为准
string属于类模板,它的基本结构参考以下代码:
//动态增长字符数组
template <class T>
class basic_string
{
private:
T* _str;
size_t _size;
size_t _capacity;
};
typedef basic_string<char> string;
构造函数
void test1()
{
//无参构造,空字符串
string s1;
//带参构造
string s2("我在学习string");
//隐式构造+拷贝构造
string s3 = "我在学习string";
string s4(10,'#');
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
cout << s4 << endl;
string s5(s3);
string s6 = s3;
cout << s5 << endl;
cout << s6 << endl;
string s7("hello world", 5);
cout << s7 << endl;
//s7->hello
string s8(s7, 2, 3);
cout << s8 << endl;
//llo
string s9(s7, 2, 30);
cout << s9 << endl;
//llo
string s10(s7, 2);
cout << s10 << endl;
//llo
}
npos是-1,但是这里是无符号,实际并不是-1,是4294967295。
对于s9很明显s7(hello),长度没有30个字符;那么默认就会以str末尾之前的字符进行构造
string类对象可支持直接用cin和cout进行输入和输出,这是因为重载了流插入>>和流提取<<操作符
容量操作
函数名称 | 功能说明 |
---|---|
size(重点) | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty (重点) | 检测字符串释放为空串,是返回true,否则返回false |
clear (重点) | 清空有效字符 |
reserve (重点) | 为字符串预留空间 |
resize (重点) | 将有效字符的个数该成n个,多出的空间用字符c填充 |
void test5()
{
//reserve测试
//TestPushBack();
//resize 将有效字符的个数修改成n个,多出的空间用字符c填充
string s1("www.ahao.com");
s1.resize(3);//删除数据
cout << s1.size() << endl;
cout << s1.capacity() << endl;
cout << s1 << endl << endl;
string s2("www.ahao.com");
s2.resize(15,'n');//不扩容增加数据
cout << s2.size() << endl;
cout << s2.capacity() << endl;
cout << s2 << endl << endl;
string s3("www.ahao.com");
s3.resize(18,'c');//扩容+插入
cout << s3.size() << endl;
cout << s3.capacity() << endl;
cout << s3 << endl << endl;
}
size()和length()两个接口底层是完全一样的
- 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不会改变容量大小
扩容问题:
void TestPushBack()
{
string s;
//为字符串预留500字节的空间
s.reserve(500);
size_t sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
cout << s.size() << endl;
cout << "making s grow:\n";
for (int i = 0; i < 1000; ++i)
{
s.push_back('c');
if (sz != s.capacity())
{
sz = s.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
不同平台下扩容规则不同,windows下vs2019是1.5倍扩容;linux下是2倍扩容
扩容也会有开销,如果我们提前知道需要多少空间,就可以使用reserve(n)开好空间
访问及遍历操作
函数名称 | 功能说明 |
---|---|
operator[] | 返回pos位置的字符,const string类对象调用 |
begin+ end | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
rbegin + rend | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
范围for | C++11支持更简洁的范围for的新遍历方式 |
void test2()//字符串遍历
{
string s1("1234");
// 遍历他
cout << s1.size() << endl;
// 1、下标 []
for (size_t i = 0; i < s1.size(); ++i)
{
s1[i]++;
}
cout << s1 << endl;
// 2、范围for
for (auto& ch : s1)
{
ch--;
}
cout << s1 << endl;
//3.迭代器 ----通用的访问形式
//it1可以理解成一个指针,指向字符串第一个元素的位置
string::iterator it1 = s1.begin();
//s1.end()返回有效数据的下一个位置
while (it1 != s1.end())
{
*it1 += 1;
++it1;
}
it1 = s1.begin();
while (it1 != s1.end())
{
cout << *it1 << endl;
++it1;
}
}
迭代器
迭代器行为上像指针,但是却不一定是指针。
迭代器的意义在于通用,所有容器都可以使用迭代器这种方式去进行遍历和修改。
正向迭代器:begin+ end与反向迭代器rbegin+rend
rebegin()返回指向字符串最后一个字符(即其反向开头)的反向迭代器。
rend()返回一个反向迭代器,该迭代器指向字符串第一个字符(被视为其反向结尾)之前的理论元素。
反向迭代器向后迭代:增加它们会将它们移动到字符串的开头。
void test3()
{
string s1("1234");
//不加const
string::iterator it1 = s1.begin();
//auto it1=s1.begin();
while (it1 != s1.end())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
//反向迭代器
string::reverse_iterator rit1 = s1.rbegin();
while (rit1 != s1.rend())
{
cout << *rit1 << " ";
++rit1;
}
cout << endl;
}
const迭代器
普通的迭代器可读可写,而const迭代器可读不可写,因为const修饰this指向的内容,不能改变
// 正向 反向 -- 可以遍历读写容器数据
// const正向 const反向 -- 只能遍历,不能修改容器数据
void Print(const string& s)
{
//遍历读,不能写
//string::const_iterator it = s.begin();
//const string::iterator it = s.begin();错误!这里保护的是it本身,即it不能++,但是数据可以变
auto it=s.begin();
while (it != s.end() )
{
//*it += 1;不能改变内容
cout << *it << endl;
++it;
}
cout << endl;
//反向
string::const_reverse_iterator rit = s.rbegin();
//auto rit = s.rbegin();
while (rit != s.rend())
{
cout << *rit << endl;
++rit;
}
}
c++11为了区别了普通迭代器和const迭代器提供了四个新的迭代器,本质和begin的const版本一样
修改操作
函数名称 | 功能说明 |
---|---|
push_back | 在字符串后尾插字符c |
append | 在字符串后追加一个字符串 |
operator+=(最好用) | 在字符串后追加字符串str |
c_str | 返回C格式字符串 |
find + npos | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
assign | 为字符串分配一个新值,替换其当前内容 |
void test6()
{
string s1("www.ahao.com");
s1 += ' ';
s1 += '!';
s1 += "hello world";
cout << s1 << endl;
string s2("!!!!!!!");
s1 += s2;
cout << s1 << endl;
}
void test7()
{
string s("www.aho.com");
s.insert(0,"https/");//指定位置插入
cout << s << endl;
s.insert(6,"S");
cout << s << endl;
s.erase(0,6);//指定位置删除
cout << s << endl;
s.erase(3);
cout << s << endl;
string s1("hello world hello world");
string s2("hello world hello world");
string s3(s2);
string s4(s3);
s1.assign("www bbbb",5); //清空原来的,赋值替换
cout << s1 << endl;//www b
s2.replace(6, 3, "wmh");//从6开始替换三个字符
cout << s2 << endl;//hello wmhld hello world
// 将' '替换成空格
size_t pos = s3.find(' ');
while (pos != string::npos)
{
s3.replace(pos, 1, "20%");
pos = s3.find(' ', pos + 3);
}
cout << s3 << endl;
//查看文件内容:文件名不支持c++类型的字符串,只能转换成c类型的
string file("test.cpp");
FILE* f = fopen(file.c_str(),"r");//返回string 底层的char* 指针
assert(f);
char ch = fgetc(f);
while (ch != EOF)
{
cout << ch;
ch = fgetc(f);
}
fclose(f);
string file;
cin >> file;
//取文件名后缀 test.cpp 或 test.tat.ccr.bat
//size_t pos = file.find('.');//从前往后找第一个“.”的位置
size_t pos = file.rfind('.');//从后往前找第一个“.”的位置下标
if (pos != string::npos)
{
cout << pos << endl;
string str = file.substr(pos);//返回pos及其位置之后的字符串
cout << str << endl;
}
}
非成员函数重载
关系运算符重载
void test8()
{
string str("aaaaa");
const char* s = "bbbbbbb";
str == s;//bool operator== (const string & lhs, const char* rhs);
s == str;//调用bool operator== (const char* lhs, const string& rhs);
}
getline
当我们使用cin读取一个字符串的时候,当遇到" "或"\n"
的时候会结束,但是当我们需要读取AA bbb cc
这样一字符串,很显然cin已无法满足,需要借助getline,它只会读取到\n才会结束
如求最好一个单词的长度
#include <iostream>
using namespace std;
#include <string>
int main() {
string str;
getline(cin,str);
size_t pos = str.rfind(' ');
cout<<str.size()-pos-1<<endl;
}
三、总结
关于string的使用就先讲到这里吧,对于string,只使用的话借助使用文档就可以很轻松的使用。想要了解更多C++方面的知识,关注我!!!