string类
standard template libaray 标准模板库,简写为STL;起始C++标准库的组成部分,STL 实现了数据结构和算法的标准化,提高了代码的复用性、可维护性和效率,开发者可以直接使用其中的组件,无需重复开发。1.字符串是表示字符序列的类2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。3. string类是使
希望文章能对你有所帮助,有不足的地方请在评论区留言指正,一起交流学习!
目录
本文先简单介绍STL,然后对string的成员函数以及关于string的非成员函数做一些介绍,本文的string成员函数采用的C++98版本。
1.STL简介?
1.1.STL介绍
standard template libaray 标准模板库,简写为STL;起始C++标准库的组成部分,STL 实现了数据结构和算法的标准化,提高了代码的复用性、可维护性和效率,开发者可以直接使用其中的组件,无需重复开发。
1.2.STL 的版本
(1)原始版本 由Alexander Stepanov、Meng Lee 在惠普实验室完成的,将其开源,允许任何人运用、拷贝,使用原始版本所编写的代码也是要开源使用,HP版本是STL实现版本的始祖。
(2)P.J版本 由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低, 符号命名比较怪异。
(3)RW版本 由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。
1.3.STL的组成
- 容器(Containers):用于存储数据的对象,如向量(vector)、链表(list)、映射(map)等,提供了不同的数据结构实现。
- 算法(Algorithms):一系列用于操作容器中数据的函数,如排序(sort)、查找(find)、复制(copy)等。
- 迭代器(Iterators):连接容器和算法的桥梁,它的行为类似指针,使得算法可以独立于具体的容器类型进行操作。
- 函数对象(Function Objects):也称为仿函数,是一种行为类似函数的对象,常被用作算法的参数,以定制算法的操作。
- 配接器(Adapter):包装已有组件以改变接口,配接器的作用是修改已有组件的接口形式,使其满足新的需求,而无需修改原有组件的实现。分为容器、迭代器、函数配接器。
- 空间配置器是 STL 中负责内存管理的组件,它为容器提供内存的分配、释放、对象构造和析构等操作,使容器无需直接处理内存细节。类如内存池。
2.string类介绍
1.字符串是表示字符序列的类2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型。4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
1. string是表示字符串的字符串类2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;4. 不能操作多字节或者变长字符的序列
3.string类常用接口说明
本小节将对string的成员函数进行讲解,表格中是函数的基本使用方式若有特殊的使用方式或者不同的重载函数会另外的说明。
3.1.构造函数(包含拷贝构造)
| 函数名称 | 功能 |
| string(); | 默认构造:创建空字符串 |
| string( const string& str ) | 拷贝构造:用已有 string 对象 str 拷贝构造新对象 |
| string( const string& str, size_t pos, size_t len = npos ) |
子串构造:从 |
| string( const char* s); | C 风格字符串构造:用 C 风格字符串(char* 类型,以 \0 结尾)构造 |
| string( const char* s , size_t n); | 指定长度 C 风格字符串构造:用 s 指向的字符串前 n 个字符构造 |
| string( size_t n, char c); | 填充构造:创建包含 n 个字符 c 的字符串 |
上述表格中标黑的四个函数为重点掌握和记忆,后续表格也是如此。不再过多的重复。
//构造函数
string s1;// 创建一个长度为零空字符串
// C风格字符串构造
string s4("hello world");
//拷贝构造函数
string s2(s4); // s2 初始化为 hello world
string s3(s4,6,5);// 按数组数 s3初始化为 world
//指定长度 C 风格字符串构造
string s5("hello world",5); //将 s5 初始化为 hello
//填充构造
string s6(5, 'a'); //s6初始化为 aaaaa
//范围构造 没整明白
3.2.析构函数
| ~string() |
销毁字符串对象。 |
没有什么特别,不做多余赘述。
3.3.拷贝赋值运算符重载函数
| 函数名称 | 功能 |
| string& operator= (const string& str) | 用另一个 std::string 对象 str 赋值,将当前字符串内容替换为 str 的拷贝,返回自身引用支持链式赋值 |
| string& operator= (const char* s) | 用 C 风格字符串(const char* 类型,以 \0 结尾 )s 赋值,把当前字符串内容替换为 s 指向的字符串 |
| string& operator= (char c) | 用单个字符 c 赋值,当前字符串会被置为仅包含该字符 |
string s1;
s1 = "hello world";
string s2("张三");
s2 = s1;
string s3;
s3 = 'a';
3.4.string类对象的容量操作
| 函数名称 | 功能 |
| size | 返回字符串有效长度,单位字节。 |
| length | 返回字符串有效长度。 |
| max_size | 返回字符串可以达到的最大长度。 |
| resize |
将有效字符的个数该成n个,多出的空间用字符c填充
|
|
capacity |
返回创建对象的空间的大小 |
| reserve | 为字符串预留空间 |
| clear | 清空有效字符 |
| empty | 检测字符串是否释放为空,为空返回true,否则返回false |
| shrink_to_fit | 请求字符串减小其容量以适应其大小 |
string s1("hello world");
cout << s1.size() << endl;
cout << s1.length() << endl;
cout << s1.max_size() << endl;
cout << s1.capacity() << endl;

max_size()返回一个size_t类型的值,这个值表示std::string最多可以包含的字符数量(包括结尾的空字符'\0',不过在计算字符串有效长度时,空字符不计入)。它的大小取决于系统的内存管理机制、可用内存总量以及std::string实现方式等因素。
string s1;
cout << s1.capacity() << endl;
int old_capacity = s1.capacity();
int i = 100;
while (i--)
{
s1 += 'x';//
if (old_capacity != s1.capacity())
{
cout << "扩容:" << s1.capacity() << endl;
old_capacity = s1.capacity();
}
}
对于capacity看其中的扩容问题,在不同的编译器扩容是不一样的。
string s1;
cout << s1.capacity() << endl;
int old_capacity = s1.capacity();
int i = 100;
while (i--)
{
s1 += 'x';//
if (old_capacity != s1.capacity())
{
cout << "扩容:" << s1.capacity() << endl;
old_capacity = s1.capacity();
}
}

在vs2017的情况下,起初扩容为 二倍,再次扩容就会变为1.5倍。没有将\0算入其中实际为 16 32 48 71 ……。
void resize (size_t n);
void resize (size_t n, char c);
调整字符串大小将字符串的大小调整为 n 个字符的长度。
如果 n 小于当前字符串长度,则当前值将缩短为其前 n 个字符,删除第 n个字符之外的字符。
如果 n 大于当前字符串长度,则通过在末尾插入所需字符数来扩展当前内容,以达到 n 的大小。如果指定了 c,则新元素将初始化为 c 的副本,否则,它们是值初始化的字符(空字符)。
s1.resize(6);
cout << s1 << endl;
cout << s1.size() << endl;
s1.resize(10, 'c');
cout << s1 << endl;
cout << s1.size() << endl;

string s1("hello world");
cout << s1 << endl;
cout << s1.empty() << endl;
s1.clear();
cout << s1 << endl;
cout << s1.empty() << endl;

s1 = "111111111111111111111111111111";
cout << s1.capacity() << endl;
s1.clear();
s1.shrink_to_fit();
cout << s1.capacity() << endl;

缩容函数可以缩小字符串的容量,但是一般情况下是不建议缩小容量的,相当于开辟新的空间,然后将数据移动到新开辟的空间。
reserve函数,由于先前知道字符串大概的大小,可以为string设定一个固定的容量。
string s1;
s1.reserve(100);
cout << s1.capacity() << endl;
虽然reserve扩容的固定值是100,但是其结果是111。对于不同的平台不同的版本的C++模板库,其扩容的大小也是不一样,对于Linux来说就是100。

注意:
- 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在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。 resize是不会缩容的。
- reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于 string的底层空间总大小时,reserver不会改变容量大小。也不会缩小容量。
string s1;
cout << s1.capacity() << endl;
s1.reserve(10);
cout << s1.capacity() << endl;

如上所述,当reserve开辟的预留空间小于底层空间总大小时候,不会改变容量大小。
当字符串已经开辟的字符串的大小大于reserve指定的大小的情况,在vs中是不会改变reserve的大小的,但是在Linux中是会缩小的。当然不能小于字符串的大小。
当 n 小于当前字符串实际占用的容量(即 “请求缩小容量” 场景):reserve 是非强制(non-binding)的,编译器 / 标准库可选择忽略,字符串最终容量可能仍大于 n,不会强制压缩到 n 。
上述容量的操作中,除去shrink_to_fit可以适当的缩小容量,其他的都不行。
3.5.string类对象的访问以及遍历操作(包含迭代器)
| 函数名称 | 功能 |
| operator[ ] |
返回pos位置的字符,const string类对象调用 |
|
at( ) |
和operator [ ] 类似,但是at可以检查异常例如越界访问 |
|
begin 和end |
begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
| rbegin 和 rend |
rend获取一个字符的迭代器 + rbegin获取最后一个字符下一个位置的迭代器 |
| 范围 for |
C++11支持更简洁的范围for的新遍历方式 |
oprator [ ] 和at [ ] 例子,二者支持对字符串的修改。
string s1("hello world");
cout << s1 << endl;
int i = 0;
for (i = 0; i < s1.size(); i++)
{
cout << s1[i] << " ";
s1[i]++;
}
cout << endl;
for (i = 0; i < s1.size(); i++)
{
cout << s1.at(i) << " ";
s1.at(i)++;
}
cout << endl;
cout << s1 << endl;
operator[] 和at()的不同
try
{
string s1("hello world");
s1.at(0) = 'x';
cout << s1 << endl;
s1[13]; // 暴力处理
//s1.at(13); // 温和的错误处理
}
catch (const exception& e)
{
cout << e.what() << endl;
}

对于at()函数会捕捉异常

访问的字符串位置无效。
迭代器分为正向和反向迭代器,以及const修饰this指针常量迭代器,共四种。
string s1("hello world");
string::iterator it = s1.begin();
cout << s1 << endl;
while (it != s1.end())
{
(*it)++;
it++;
}
it = s1.begin();
while (it != s1.end())
{
cout << (*it) ;
it++;
}
cout << endl;
s1 = "hello world";
cout << s1 << endl;
string::reverse_iterator rit = s1.rbegin(); // 这里可以调用auot关键字 auto rit = ……
while (rit != s1.rend())
{
(*rit)++;
rit++;
}
rit = s1.rbegin();
while (rit != s1.rend())
{
cout << (*rit);
rit++;
}
cout << endl;
上述是iterator以及 reverse_iterator迭代器,可以读取字符串的内容和修改字符串的内容;对于常量字符串 const_iterator以及 const_reverse_iterator 不在赘述,就是不能修改字符串数据。
s1 = "hello world";
cout << s1 << endl;
// 范围的for的底层也是迭代器,会调用到begin以及end函数
for (char& ch : s1)
//for (auto& ch : s1)// 在知道ch类型的情况下,可以不在使用auto
{
ch++;
cout << ch;
}
cout << endl;
iterator有可能是指针,有可能不是。iterator提供了一种统一的方式访问和修改容器数据的数据算法就可以通过迭代器,去处理容器中数据。
reverse是 STL(标准模板库)提供的一个算法函数,而string是 C++ 标准库中的一个容器类。iterator的作用就是链接容器类和算法函数
s1 = "hello world";
cout << s1 << endl;
reverse(s1.begin(), s1.end());
cout << s1 << endl;
reverse(s1.rbegin(), s1.rend());
cout << s1 << endl;
3.6.string类对象的修改
| 函数名称 | 功能说明 |
| operator+= | 在字符串后追加字符串str |
| append | 在字符串后面追加一个字符串 |
| push_back |
在字符串后尾插字符c
|
| assign | 为字符串分配一个新值,替换其当前内容。 |
| insert | 在字符串中插入其他字符,就在 pos(或 p)指示的字符之前 |
| erase | 擦除字符串的一部分,减少其长度 |
| replace | 将字符串中以字符 pos 开头并跨 len 字符的部分(或字符串中介于 [i1,i2] 之间的部分]替换为新内容 |
| swap | 通过 str 的内容交换容器的内容 |
| pop_back | 删除最后一个字符 |
string s1("hello ");
cout << s1 << endl;
s1.push_back('w');
cout << s1 << endl;
s1.append("or");
cout << s1 << endl;
s1 += "ld";
cout << s1 << endl;
operator+=是调用push_back或者append,构成函数重载。当然append函数有许多的重载类型。其他的字符串操作类型使用较少,可以根据使用情况自己看手册。一般情况下不会使用头插和中间插入和中间修改,因为其有 时间复杂度。
string s1("hello world");
cout << s1 << endl;
s1.assign("date");
cout << s1 << endl;
较为没用,本身具有赋值,但是意义不大,assign就是覆盖原先的值。
// 插入
s1.insert(0, "day ");
cout << s1 << endl;
谨慎使用因为其时间复杂度较高。
s1.erase(3, 1);
cout << s1 << endl;
3为删除的起始位置 1为删除几个字符。不建议使用。replace建议不要使用,使用迭代器或者范围for寻找到位置直接修改即可。
3.7字符串操作函数
这里只是将用过的列举出来
| 函数名字 | 功能说明 |
| c_str | 返回指向数组的指针 |
| find | 在字符串中搜索由其参数指定的序列的第一次出现。 |
| rfind | 在字符串中搜索由其参数指定的序列的最后一次出现。 |
| find_first_of | 在字符串中搜索与其参数中指定的任何字符匹配的第一个字符。 |
| find_last_of |
在字符串中搜索与其参数中指定的任何字符匹配的最后一个字符。 |
| find_first_not_of | 在字符串中搜索与其参数中指定的任何字符都不匹配的第一个字符。 |
| find_last_not_of | 在字符串中搜索与其参数中指定的任何字符都不匹配的最后一个字符。 |
| substr | 返回一个新构造的对象,其值初始化为该对象的子字符串的副本。 |
| compare | 将字符串对象(或子字符串)的值与其参数指定的字符序列进行比较。 |
为了更好的和C的一些接口进行配合 返回字符串指针,
string filename = "ht.txt";
FILE* fl = fopen(filename.c_str(), "r");
其次是在C++的string中修改方便。
将fing函数和substr函数进行举例说明。
string ur1("https ://blog.csdn.net/csdnnews/article/details/150348710? spm=1000.2115.3001.5926");
size_t pos1 = ur1.find("://");
string protocol;
if (pos1 != string::npos)
{
protocol = ur1.substr(0, pos1);
}
cout << protocol << endl;
string domain;
string uri;
size_t pos2 = ur1.find('/', pos1 +3);
if (pos2 != string::npos)
{
domain = ur1.substr(pos1 + 3, pos2 - (pos1 + 3));
}
cout << domain << endl;
uri = ur1.substr(pos2 + 1);
cout << uri << endl;
rfind和find是反着的的。后面的几个函数实际用处不大,
3.8.string非成员函数
| 函数名称 | 功能 |
| operator+ |
尽量少用,因为传值返回,导致深拷贝效率低
|
| relational operators |
大小比较
|
| swap | 交换 |
| operatro>> |
输入运算符重载
|
| operatro<< |
输出运算符重载
|
| getline |
获取一行字符串
|
注意operator+字符串自身不会改变 getline的使用是遇到换行符的时候再读取结束,平时的>>流提取,遇到空格或者换行符都会结束。
string s1("hello ");
s1 + 'w';
cout << s1 << endl;
s1.clear();
cout << s1 << endl;
cin >> s1;
cout << s1 << endl;
s1.clear();
getline (cin,s1);
cout << s1 << endl;
上述是string的部分重要的函数,若有不足,可以提出,后续也会有补充的。
更多推荐

所有评论(0)