C++:STL详解(一)string类的基本介绍与使用方式

Blog’s 主页: 白乐天_ξ( ✿>◡❛)

🌈 个人Motto:实践是检验真理的唯一标准!!!敲代码需要勤快点!!!!

💫 欢迎来到我的学习笔记!

参考文章:【C++】深入浅出STL之string类

一、C/C++中的字符串

1.1 C语言中的字符串

首先我们需要思考:为什么要学习string类?

  • string意为字符串(相当于一个字符顺序表)。在C语言中,字符串是由一系列字符组成,并以'\0'作为结束标志的字符数组,我们想要存储字符串时就要用到字符数组。为了操作方便成员的标准库中提供了一些str系列的库函数。
  • 但是这些库函数与字符串是分离开的,不太符合OPP的思想,而且底层空间需要用户自己去管理,稍不注意可能会越界访问。

1.2 string类的使用场景

  • OJ中,有关字符串的题目基本是以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类**,很少有人使用C语言标准库中的字符串操作函数**。
  • ……

因此,在C++中专门设计了一个与字符串有关的类。我们知道,C++是面向对象的,我们可以在类里面去写各种成员函数来对外提供操作字符串的接口,这个类就是<u>string类</u>

二、初识string类

2.1 概述

+ 常用学习文档网站:[cplusplus.com](https://legacy.cplusplus.com/)(非官方网站) + 根据string类的文档我们可以看出string的确是一个类,由**类模板**`basic_string`实例化得来的。

根据上面文档的内容,可知string相当于一种字符顺序表/字符数组,可以发生动态增长。

  • 由下图可知,basic_string类是一个类模板。

  • 该类模板可以实例化出很多的模板类(如下图),其中就包括我们要学习的string类

  • 从上图我们还可以看见除了string的其他的类,这些都是编码引起的。我们比较常见的ASCII编码(全称:【美国信息交换标准代码】)主要是大小写中英字母、数字、标点符号等128个字符。下图是ASCII码的映射表,一个ASCII码值就对应一个字符。
    • 其实在VS编译器下的内存中我们可以发现:
  • 但是ASCII不能表示出其他国家的字符例如中国汉字,为了表示出其他各国字符,推出了万国码(Unicode),也叫统一码。我国也推出一套编码字符集,叫做:GBK。在【GB2312-80】中就存储了很多有关汉字的规则。

2.2 string类的特性

  1. string类是表示字符串的字符串类;
  2. 该类的接口与常规容器的接口基本相同,再添加了一些转么能用来操作string的常规操作。
  3. string在底层实际是:basic_string 模板类的别名,typedef basic_string<char,char_trait , alloctor> string;
  4. 不能操作多字节或者变长字符的序列。

注意: 在使用string类时,必须包含#include头文件以及using namespace std;

三、string类的接口

对于接口类的学习,我们只需要熟悉比较常用的即可,其他的接口可以根据类似的方法去学习。

3.1 string类的默认成员函数

函数名称功能说明
constructor构造函数
destructor析构函数
operator=赋值重载

3.1.1 构造函数--自动调用

  • 构造函数constructor一共有7个重载形式,其中比较重点的有以下三个函数。

  • 函数原型:
string();                                                // 
string(const string& str);                               // str全部拷贝
string(const string& str, size_t pos, size_t len = npos);// str从pos开始拷贝len个字符(拷贝一部分)
string(const char* s);                                   //
string(const char* s, size_t n);
string(size_t n, char c);template <class InputIterator>
string(InputIterator first, InputIterator last);
  • 函数运用:
  1. string(); 无参常用
#include <iostream>
using namespace std;
int main()
{string s1;//构造空字符串cout << s1 << endl;return 0;
}

运行结果:

  1. string(const string& str);
#include <iostream>
using namespace std;
int main()
{string s1("Hello world");string s2(s1);cout << s2 << endl;return 0;
}

运行结果:

  1. string(const string& str, size_t pos, size_t len = npos);拷贝str字符串中从pos位置开始的len个字符。
// 拷贝str字符串中从pos位置开始的len个字符
//string (const string& str, size_t pos, size_t len = npos);	
//npos:在文档中查看:无符号整数的最大值
//npos是一个缺省值,根据文档:static const size_t npos = -1,npos存储整型最大值(缺省值),
//因此字符串是最大整型数据的长度(4G),它想表示:字符串有多长,就取多长
#include <iostream>
using namespace std;
int main()
{string s1("Hello world");string s2(s1, 6, 5);cout << s2 << endl;return 0;
}

运行结果:

有一个参数npos,在文档中查看,得知它是无符号整形的最大值npos是一个缺省参数,在函数中不传入参数值就表示无论字符串有多长,他就会取多长。

继续查看下面的文档,意思是:**从pos位置的len个长度取拷贝字符串的一部分(如果str字符串太短或者len为npos则直接到达字符串末尾)。**由此可知,即使不传入npos参数,字符串也会拷贝到末尾。

我们可以打印查看npos的值。下面是在VS编译器debug X64环境下的输出结果。

cout << string::npos << endl;

18446744073709551615

debug X86环境下的运行结果如下:

4294967295

Linux平台下的g++编译器环境下,结果又是不一样的。
4. string(const char* s);

#include <iostream>
using namespace std;
int main()
{string s1("Hello world!");cout << s1 << endl;return 0;
}

运行结果:

  1. string(const char* s, size_t n);
#include <iostream>
using namespace std;
int main()
{string s1("Hello world!", 3);cout << s1 << endl;return 0;
}

运行结果:

  1. string(size_t n, char c);
#include <iostream>
using namespace std;
int main()
{string s1(6, 'A');cout << s1 << endl;return 0;
}

运行结果:

  1. template <class InputIterator> string(InputIterator first, InputIterator last);

3.1.2 赋值重载

  • 赋值重载的内容我在C++:缺省参数|函数重载|引用|const引用中讲过,可以跳转去了解一下。

string& operator= (const string& str);	// 将一个string对象赋值给到另一个
string& operator= (const char* s);		// 将一个字符串赋值给到string对象
string& operator= (char c);				// 将一个字符赋值给到string对象

3.2 string类对象的常见容量操作

下面我将介绍string类中有关容量的一些操作。

函数名称功能说明
size返回字符串有效字符长度
length返回字符串有效字符长度
capacity返回总空间大小
max_size返回字符串的最大长度
resize将有效字符的个数改成n个,多出的空间用字符c填充
reserve为字符串预留空间
clear清空有效字符
empty检测字符串释放为空串,是返回true,否则返回false
shrink_to_fit收缩到合适大小

首先介绍一下【size】的内容!

① size

+ `size`表示当前字符串已经存放了多少数据,`capacity`表示当前字符串可容纳的空间数。 + 我们在VS编译器下进行观察,可以发现str明明是一个空字符出纳,里面并没有数据,而VS为它开辟了大小默认15的空间。其实这里本应该是16,只不过`\0`也占了一个大小。

  • 然后去构建一个具体的字符串来进行观察,发现size的值发生了变化。我们发现sizelength的值是一样的。
#include <string>
#include <iostream>
using namespace std;
int main()
{string s("Hello world!");cout << s.size() << endl;cout << s.length() << endl;cout << s.capacity() << endl;cout << s << endl;return 0;
}

运行结果:

  • 因此我们开始在文档中查看他们的相关性质,发现他们的性质也是一样的。

  • 我们通过观察文档发现,C++容器中并没有string,这是因为:在STL的诞生历史中,string类并不属于STL,它是STL之前就存在的东西,属于C++标准库里面的东西。

  • 根据向前兼容,由于string更早使用,而且使用length来定义长度接口,因此即使STL出来了也会有人在用string的东西比如length,因此没有淘汰掉过去的东西。而且在“数”里面,length就不是很合理,而STL中的size就具有通用性。向前兼容、向前发展,因此,旧的要兼容新的,继续保留,存在即合理!

画板

② capacity

下面介绍一些【capacity】相关的内容。

  • capacity表示当前字符串可容纳的空间数。它可以用于容量获取,容量没有算入\0,空间大小比实际容量多一个。
void TestPushBack()
{string s;size_t sz = s.capacity();// 保存最初的容量cout << "capacity changed: " << sz << "\n";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";}}
}
  • VS2022编译器环境下的运行结果:实际空间其实还要加一,给\0。(第一次是2倍扩容,后续是1.5倍扩容
    • Linux–g++4.8平台下的运行结果:(视频截图)(扩容呈标准的2倍趋势)

  • 为什么在不同的平台环境下capacity扩容的趋势特点是不一样的呢?
    • 因为在不同平台下的STL库是不一样的;在VS中,使用不同版本的VS大小也是不一样的。
    • VS下做了特殊处理:数据大小小于16的数据存入内部的buff中;大于等于16时,就存入string_str指向的空间,这样可以避免小块内存的数据被存入堆上。 (原始视图里面可以看真实的底层情况)
  • 问题:扩容一般会出现两个问题:一次性扩容太多,会出现空间资源的浪费;扩容太少,又会出现频繁扩容的情况,毕竟扩容是有消耗的。因此,引入reservereverse。跳转到相应位置学习。
//string的底层结构:
class string
{
private:char  _buff[16];//<16char* _str;//>=16size_t _size;size_t _capacity;
};

③ max_size

下面将介绍【max_size】的相关内容。

void Test_Max_Size()
{string s("Hello world!");cout << s.size() << endl;cout << s.max_size() << endl;
}
  • 我们在不同的平台下进行测试,发现结果各不相同。
    • VS编译器X86环境下的运行结果:

Linux平台下的运行结果和上面是不一样的。

④ clear

接下来是【clear】的相关内容!

  • clear一般是不会清理容量的,一般都是清理数据。
void TestClear1()
{string s("Hello BaiLetian!");cout << "s.size()=" << s.size() << endl;cout << "s.capacity()=" << s.capacity() << endl << endl;s.clear();cout << "s.size()=" << s.size() << endl;cout << "s.capacity()=" << s.capacity() << endl << endl;
}

运行结果:

⑤ empty

在这里插入图片描述

  • 判断字符是否为空字符,若已经清空,返回1;若没有清,返回0。
void TestClear() 
{string s("Hello");cout << "size: " << s.size() << endl;cout << "capacity:" << s.capacity() << endl;cout << s.empty() << endl;s.clear();cout << "size: " << s.size() << endl;cout << "capacity:" << s.capacity() << endl;cout << s.empty() << endl;
}

运行结果:

⑥ reserve

  • reserve :保留、预留,一般不会缩容。根据文档:
    • 提前开辟空间,避免扩容。(扩容忙有消耗,频繁扩容消耗很大)
    • reserve一般不会缩容的:增加容量到n,或者
    • reserve扩容有一个特例:申请扩容容量较小,不会进行扩容;申请扩容容量较大,才会进行扩容。
void TestString2()
{string s("Hello world!XXXXXXXXXXXX");cout << s.size() << endl;cout << s.capacity() << endl << endl;s.reserve(20);//扩容至少20cout << s.size() << endl;cout << s.capacity() << endl <<endl;s.reserve(28);cout << s.size() << endl;cout << s.capacity() << endl << endl;s.reserve(40);cout << s.size() << endl;cout << s.capacity() << endl << endl;//申请容量较小,不会动;申请容量较大,才会扩容
}

VS编译器的运行结果:(VS不会缩容,会牺牲空间)

    * Linux-g++环境下的运行结果:(对齐的,会进行缩容)

  • reverse:反转、逆置。

void TestPushBack()
{//观察容量变化string s;s.reserve(100);//功能:开辟100 个容量的空间,实际上可以开辟比它还要大好几倍的量的空间(做整数倍对齐)size_t sz = s.capacity();// 保存最初的容量,这里的capacity并不包含\0cout << "capacity changed: " << sz << "\n";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";}}
}

VS下的reserve用法:(实际上开辟的空间数量比给出的数字还要大)VS需要保持整数倍对齐。

- Linux的g++,reserve的用法:

⑦ resize

在这里插入图片描述

⑧ shrink_to_fit

  • capacity减少到size。

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

函数名称功能说明
operator[ ] 访问(并可以修改)**pos**位置的字符,const string类对象调用
begin + end + 迭代器begin获取第一个字符的迭代器+end获取最后一个字符串下一个位置的迭代器
rbegin + rend + 迭代器rbegin获取最后一个字符的迭代器+rend获取第一个字符前一个位置的迭代器
范围forC++11支持更加简洁的范围for遍历
autoauto在C++98和C++11的不同遍历

3.3.1 operator[]访问+遍历(⭐)

  1. operator[]:访问pos位置的字符,可以通过它来遍历数据。下面是它的两种形式。(主要与运算符重载这部分知识有关)(这部分知识在7月17日末尾讲了)
	  char& operator[] (size_t pos);		//可读可写版本
const char& operator[] (size_t pos) const;	//只读版本
class string
{
public:char& operator[](size_t i){return _str[i];//返回引用:不是为了减少拷贝,而是为了修改数据}
private:char* _str;size_t _size;size_t _capacity;
};
  1. 运用:
void TestString1()
{string s("hello world!");cout << s << endl;// 1.下标+[]重载for (size_t i = 0; i < s.size(); i++){cout << s[i] << " ";}cout << endl;//换行
}

运行结果:

  1. 可以用operator[]来修改pos位置的数据。
void TestString1()
{string s("hello world!");cout << s << endl;for (size_t i = 0; i < s.size(); i++){s[i] += 1;//cout << s[i] << " ";}cout << endl;//换行
}

运行结果:

  1. 我们还可以把数据进行++操作后,再进行--操作。
void TestString1()
{string s("hello world!");cout << s << endl;for (size_t i = 0; i < s.size(); i++){s[i] += 1;//cout << s[i] << " ";}cout << endl;//换行
}

运行结果:

  1. 但是上面这种重载后的[]的用法和我们最初使用的数组中的[]是不一样的。
string s("abcdef");
char s2[] = "hello world";
s[1]++;		// -> operator[](1)++
s2[1]++;	// -> *(s2 + 1)++

3.3.2 四种迭代器(用于遍历)

1. 迭代器有四种:普通对象的迭代器、普通对象的反向迭代器、const对象的迭代器、const对象的反向迭代器。 2. 迭代器提供了访问所有容器的通用的访问方式,所有的容器都可以用它来访问。例如链表:
#include <list>
void TestList()
{list<int> lt = { 1,2,3,4,5,6,7 };list<int>::iterator lit = lt.begin();while (lit != lt.end()){cout << *lit << " ";++lit;}cout << endl;
}
int main()
{TestList();return 0;
}

运行结果:

1 2 3 4 5 6 7

  1. 对于上面的代码:
    • string_str指向这一串数据。
    • 使用迭代器定义litlit有点像指针,但是它并不是指针。
    • 规定end()返回最后一个有效字符的下一个位置。
    • 规定begin()是返回开始位置的迭代器。

① 普通对象的迭代器(可读可写)—— iterator

void TestIterator1()
{// 1.普通对象的迭代器(可读可写)——iteratorstring s2("Hello bailetian!");string::iterator it = s2.begin();while (it != s2.end()){cout << *it << " ";++it;}cout << endl;
}

运行结果:

H e l l o b a i l e t i a n !(有空格)

② 普通对象的反向迭代器(只读)——reverse_iterator

void TestIterator2()
{string s2("Hello bailetian!");// 2.普通对象的反向(r)迭代器——const_iteratorstring::reverse_iterator rit = s2.rbegin();while (rit != s2.rend()){cout << *rit << " ";++rit;//仍然是 ++ 操作,但是它是反的,倒着输出打印}cout << endl;
}

运行结果:

! n a i t e l i a b o l l e H

③ const对象的迭代器——const_iterator

void TestIterator3()
{// 3.const对象的迭代器:只读不写(不可修改指向的内容)——reverse_iteratorconst string s2("Hello bailetian!");string::const_iterator cit = s2.begin();while (cit != s2.end()){//*cit += 2;// error c3892: “cit”: 不能给常量赋值(*cit是常量)cout << *cit << " ";++cit;}cout << endl;
}

运行结果:

H e l l o b a i l e t i a n !

④ const对象的反向迭代器——const_reverse_iterator

void TestIterator4()
{// 4.const对象的(是“的”而不是“修饰”)反向(r)迭代器——const_reverse_iteratorconst string s2("Hello bailetian!");string::const_reverse_iterator rcit = s2.rbegin();// 类型名称太长:autowhile (rcit != s2.rend()){cout << *rcit << " ";++rcit;}cout << endl;
}

运行结果:

! n a i t e l i a b o l l e H

3.3.3 范围for的遍历(auto)

void TestFor1()
{string s("ABCDEF");string::iterator it = s.begin();//使用迭代器定义指针while (it != s.end())//有点像指针{*it += 2;//在这里进行迭代器的修改(迭代器可以进行修改)cout << *it << " ";++it;}cout << endl;// 3.范围for// 自动赋值,自动迭代,自动判断结束for (auto ch : s)// auto在c++11表示自动推导,在这里自动推导识别s的类型//这里是一个冒号,而不是两个{ch -= 2;//在这里对刚才的修改做出恢复cout << ch << " ";}cout << endl;cout << "s:" << s << endl;
}
  • 运行结果:

C D E F G H

C D E F G H

  1. C++11引入基于范围的for循环,范围for分为两个部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围;
  2. 范围for的底层就是迭代器,可以从汇编进行观察(汇编里面看见了begin、end等迭代器内容);
  3. 迭代器是可以进行修改的;
  4. 范围for可以作用到数组和容器对象上进行遍历。
  • 注意:在迭代器中使用了*it += 2;进行+的操作,在范围for里面进行-的操作:这里的代码有一个大坑。加入下面的代码就可以观察到:迭代器里面进行修改s就改变了,但是范围for里面进行修改s却没有发生改变。
    • 这里主要的原因就是:for这一部分的原理就是底层变化成迭代器后,相当于将*it赋值给ch,而ch本身只是某个字符的一份拷贝,即局部变量。修改局部变量时,并不会修改对应的字符。而迭代器就类似于指针。
cout << "s:" << s << endl;//在最后一行加入此代码
- 如果想要进行修改,就使用引用。此时`ch`变成`*it`里面每个字符的别名。
for(auto& ch : s)

3.3.4 auto的遍历(for)

  1. cpp98的遍历
//测试c++98和c++11的数组遍历对比——范围for用于数组遍历、容器遍历
void TestTraverse98()
{// 范围forint array[] = { 1, 2, 3, 4, 5 };// c++98的遍历:数组遍历for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i){array[i] *= 2;}for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i){cout << array[i] << " ";}
}
  1. cpp11的遍历
void TestTraverse11()
{int array[] = { 1, 2, 3, 4, 5 };// c++11的遍历:auto+范围for——是用于遍历for (auto& e : array)// 一个:,而不是两个:e *= 2;//如果是大点的对象,则使用引用,e是array[0]、array[1]……的别名for (auto e : array)cout << e << " " << endl;
}
  • 上面两段代码运行结果都是一样的:

2

4

6

8

10

3.3.5 auto的扩展

C++11提供的auto。
  1. 早期auto修饰的变量是自动变量,可以进行自动释放。后来栈上的变量都可以进行自动释放。因此此功能废弃了。
  2. C++11用auto做自动推导,用来进行类型的声明,相当于自动占位。
  3. auto存在的意义/价值:用auto来推导并替换变量的类型名称,简化代码。但是这种用法牺牲了代码的可读性:要求对被替换的内容比较熟悉,替换后能知道它是什么东西。
#include <map>
map<string, string>dict;
// map<string, string >::iterator mit = dict.begin();
auto mit = dict.begin();//替换后简化代码
// 当我们不熟悉map<string, string >时,可读性就降低了不知道auto替换的是什么类型
  1. auto不能定义无类型/ 类型不清楚的(auto通过内容推导类型,但是现在无法通过内容进行推导了)
  2. auto不能定义数组。
  3. auto不能做参数,给缺省值也不支持。

3.4 string类对象的修改操作

下面是对string类对象的修改操作。

函数名称功能说明
push_back在字符串的末尾加入一个字符
append在字符串的末尾加入一个字符
operator+=在字符串后面追加字符串str
insert在指定位置插入字符货字符串等操作
assign使用指定字符串替换原字符串
erase删除字符串中的一部分
replace替换指定区间的字符串
pop_back删除字符串的最后一个字符
swap收缩到合适大小

3.4.1 push_back

![](https://i-blog.csdnimg.cn/direct/fe19241dc17447b2a3958222b2163b78.png#pic_center)

尾插一个字符,尾插一个字符串使用的是下面的append

void TestString()
{string s("Hello world!");s.push_back(' ');s.push_back('X');cout << s << endl;}

运行结果:

3.4.2 append

string& append (const string& str);		// 追加一个string对象
// 追加一个string对象中的指定字符串长度
string& append (const string& str, size_t subpos, size_t sublen);	string& append (const char* s);				// 追加一个字符串
string& append (const char* s, size_t n);	// 追加字符串中的前n个字符串
string& append (size_t n, char c);			// 追加n个字符

append支持尾插一个字符串。根据文档一共有6种重载形式。

void TestString()
{string s("Hello world,");s.push_back(' ');s.append("BaiLetian!");cout << s << endl;
}

运行结果:

3.4.3 operator+=

其实,`operator+=` 不仅可读性强,还可以实现`append`和`push_back`的功能。
void TestAppendPopback()
{string s("Hello world!");s.push_back(' ');s.push_back('X');cout << s << endl;s += 'Y';s += ",BaiLetian!";cout << s << endl;
}

运行结果:

3.4.4 insert

// 在指定位置插入一个string对象
string& insert (size_t pos, const string& str);
// 在指定位置插入一个string对象里的一部分
string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);
// 在指定位置插入一个字符串
string& insert (size_t pos, const char* s);
// 在指定位置插入一个字符串的前n个字符
string& insert (size_t pos, const char* s, size_t n);
// 在指定位置插入n个字符
string& insert (size_t pos, size_t n, char c);
// 在指定迭代器的位置插入n个字符
void insert (iterator p, size_t n, char c);
// 在指定迭代器的位置插入一个字符,并且返回一个迭代器的位置
iterator insert (iterator p, char c);
  1. insert谨慎使用,避免效率低下。因为头插、中间插入都有可能引起数据的挪动。
void TestString3()
{string s("BaiLetian!");s.insert(0, "Hello,");//头插(0位置)cout << s << endl;
}
  • 运行结果:

  1. insert的设计比较混乱:使用时容易混乱。
  2. 频繁调用会使得空间的性能大大降低。

3.4.5 erase

功能:支持某个pos位置后的删除len个字符,如果不传入len参数,那么就会删除掉pos位置后面的所有字符。

  1. 头删:
void TestErase1()
{string s("ABCDEFGHIJK");cout << s << endl;//头删1(迭代器)(删除一个字符)s.erase(s.begin());cout << s << endl;//头删2s.erase(0, 1);cout << s << endl;
}

运行结果:

  1. 尾删
void TestErase2()
{string s("ABCDEFGHIJK");cout << s << endl;//尾删1s.erase(--s.end());cout << s << endl;//尾删2s.erase(s.size() - 1, 1);cout << s << endl;
}

运行结果:

  1. 中间位置删除
void TestErase3()
{string s("ABCDEFGHIJK");cout << s <<endl;s.erase(3, 2);cout << s << endl;
}

运行结果:

  1. 一般情况下,string的尾插只增加不删除。
  2. 频繁调用erase会使得空间的性能大大降低。

3.4.6 replace

功能:替换,pos位置开始的len个字符替换成……(string/char*/两个迭代器之间的一部分/全部)。

  1. 中间的某个位置后开始的几个字符替换成一个字符串。
void TestRepalce1()
{string s("Hello world!");cout << s << endl;s.replace(6, 6, "BaiLetian!");cout << s << endl;
}

运行结果:

  1. replace的效率也不高,频繁调用会使得空间的性能大大降低。

3.4.7 find

  1. pos位置查找,例如下面的运用(将字符串中的空格替换成某个字符串):
void TestFind()
{string s("Hello BaiLetian! Hello Qianye! ");size_t pos = s.find(' ');//查找空格字符,返回字符的位置前一个字符的数据位置(第几个字符)while (pos != string::npos){s.replace(pos, 1, "%%");//找到目标后,将该一个字符用%%替换//pos = s.find(' ');//从pos位置重新开始找空格并进行替换(找到了又继续从头开始找),改进城下面的pos = s.find(' ', pos + 2);//找到后从后面两个位置开始找(因为后面两个已经被替换了)}cout << s << endl;
}

运行结果:

注意:如果是进行大量的字符串替换,极有可能会出现:替换次数多了,会不止一次地进行扩容

  1. 改进:遍历原来的字符串,如果出现目标字符,就进行替换。唯一的缺点就是牺牲了空间。
void TestFind1()
{string s("Hello BaiLetian! Hello Qianye! ");cout << s << endl;string tmp;for (auto ch : s){if (ch == ' ')tmp += "%%";elsetmp += ch;}cout << tmp << endl;
}
  1. 还有一种更加高效的方式:使用swap函数。也可以使用reserve

3.4.8 assign

3.4.9 pop_back

功能:尾删一个字符。

void TestPopBack()
{string s("abcdef");cout << s << endl;s.pop_back();cout << s << endl;
}

运行结果:

3.4.10 swap

根据文档可知:swap涉及到了深浅拷贝的问题

3.5 类对象的其他字符串操作

函数名称功能说明
c_str返回C格式字符串
substr在str中从pos位置开始,截取n个字符,然后将其返回
find在str中从pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind在str中从pos位置开始往前找字符c,返回该字符在字符串中的位置
find_first_of从前往后找第一个匹配的字符
find_last_of从后往前找第一个匹配的字符
find_first_not_of从前往后找第一个不匹配的字符
find_last_not_of从后往前找第一个不匹配的字符

3.5.1 c_str

功能:返回底层字符串的指针。意义:它可以兼容C语言,因此可以使用C语言标准库里的各种函数等等。

void Testcstr()
{string file;cin >> file;FILE* fout = fopen(file.c_str(), "r");char ch = fgetc(fout);while (ch!=EOF){cout << ch; ch = fgetc(fout);}fclose(fout);
}

在控制板上输入“test.cpp"回车:

运行结果:

3.5.2 find

功能:找一个字符或者字符串。
void TestFind()
{string s("test.cpp.zip");size_t pos = s.find('.');cout << pos << endl;
}

运行结果:

3.5.3 rfind

功能:倒着找一个字符或者字符串。
void TestRfind()
{string s("test.cpp.zip");size_t pos = s.rfind('.');cout << pos << endl;
}

运行结果:

3.5.4 substr

功能:pos位置开始的len个字符单独构造一个string并且返回该字符/字符串。

void TestFind()
{string s("test.cpp");size_t pos = s.find('.');string suffix = s.substr(pos);//得到 . 后面的字符串cout << suffix << endl; 
}

运行结果:

3.5.5 find_first_of

功能:寻找匹配到的任意的字符。注意:不能你如果看名称猜测该关键词的作用,有出入。名字相当于“find_any_of”这个名字就比较好理解一些。

void TestFindFirstof()
{string s("abcdefghijklmn");size_t found = s.find_first_of("ajm");while (found != string::npos){s[found] = '*';found = s.find_first_of("ajm", found + 1);}cout << s << endl;
}

运行结果:

观察发现:只要是字符串"ajm"中出现的一个个字符,都被替换成了字符*,由此可知,find_first_of匹配的是字符串里面的任意一个字符。

3.5.6 find_last_of

![](https://img-blog.csdnimg.cn/img_convert/b3caae3bd63901d6faba8f42b71cb25f.png)

功能:名字变化“rfind_nay_of”就更好理解一些。

Linux中避免字符串里面的非转义字符变成转义字符,多加一个\符号。

3.5.7 find_first_not_of

3.5.8 find_last_not_of

在这里插入图片描述

喜欢的uu记得三连支持哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/424330.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docker-01 创建一个自己的镜像并运行容器

docker-01 创建一个自己的镜像并运行容器 前言 我们都知道使用Docker的镜像可以快速创建和部署应用&#xff0c;大大的节约了部署的时间。并且Docker 的镜像提供了除内核外完整的运行时环境&#xff0c;确保代码的环境一致性&#xff0c;从而不会在出现这段代码在我机器上没问…

用于遥感深度学习的7种高光谱遥感图像和标签

数据介绍 此数据集来自于GIC(GRUPO INTELIGENCIA COMPUTACIONAL )官网 直达链接&#xff0c;采用MATLAB存储为矩阵形式&#xff0c;数据集后缀为.mat形式。每一个数据分为原始图像数据和标签数据&#xff0c;标签对应码请参考官网。注&#xff1a;此数据为公开数据&#xff0c…

国产视频转换HDMI1.4转单/双MIPI DSI/CSI LT6911C芯片方案,带音频输出,QFN64封装 Lontium

LT6911C:HDMI 1.4 TO MIPI DSI/CSI 芯片简介&#xff1a; LT6911C是一款高性能的HDMI1.4转换器MIPI DSI/CSI芯片用于VR/智能手机/显示应用。对于MIPI DSI/CSI输出&#xff0c;LT6911C功能可配置单端口或双端口MIPIDSI/CSI 1高速时钟通道和1~4个高速数据通道最大1.5Gb/s/lane&am…

网络工程师学习笔记——网络互连与互联网

互联网的定义 由多个网络相互连接组成更大的网络称为互联网 常见的网络设备&#xff08;是网络拓扑结构和网络的基础&#xff09; 物理层 中继器&#xff08;是将传输的信号进行放大&#xff0c;延长传输的距离&#xff09;&#xff0c;集线器也是这样&#xff0c;但是有更多…

如何获取MySQL数据表的列信息

在数据库管理中&#xff0c;了解表的结构是至关重要的。在MySQL中&#xff0c;我们可以通过几种方式来获取数据表的列信息。这不仅可以帮助我们更好地理解表的结构&#xff0c;还可以在编写查询时提供便利。以下是三种常用的方法来获取MySQL数据表的列信息。 使用 SHOW COLUMN…

C++速通LeetCode简单第10题-翻转二叉树

递归法&#xff1a; class Solution { public:TreeNode* invertTree(TreeNode* root) {if (root nullptr) {return nullptr;}TreeNode* left invertTree(root->left);TreeNode* right invertTree(root->right);root->left right;root->right left;return roo…

AtCoder ABC369 A-D题解

比赛链接:ABC369 省流&#xff1a;A<B<D<C&#xff08;题解是按照该顺序写的&#xff09; Problem A: #include <bist/stdc.h> using namespace std; int main(){int A,B;cin>>A>>B;if(AB)cout<<1<<endl;else if(abs(A-B)%20)cout&l…

一个软件分发和下载的网站源码,带多套模板

PHP游戏应用市场APP软件下载平台网站源码手机版 可自行打包APP&#xff0c;带下载统计&#xff0c;带多套模板&#xff0c;带图文教程 代码下载&#xff1a;百度网盘

饿了么基于Flink+Paimon+StarRocks的实时湖仓探索

摘要&#xff1a;本文整理自饿了么大数据架构师、Apache Flink Contributor 王沛斌老师在8月3日 Streaming Lakehouse Meetup Online&#xff08;Paimon x StarRocks&#xff0c;共话实时湖仓架构&#xff09;上的分享。主要分为以下三个内容&#xff1a; 饿了么实时数仓演进之…

C语言-整数和浮点数在内存中的存储-详解-上

C语言-整数和浮点数在内存中的存储-详解-上 1.前言2.整数2.1无符号整数2.2原码、反码、补码符号位最大值转换过程补码的意义简化算术运算易于转换方便溢出处理 1.前言 在C语言的使用中&#xff0c;需要时刻关注数据的类型&#xff0c;不同类型交替使用可能会发生错误&#xff…

算子级血缘在金融数据环境的实践应用

在企业的数据管理领域&#xff0c;算子级血缘极大优化了脚本内部字段口径的理解与追踪。面对几十、几百乃至几千行代码的复杂脚本&#xff0c;并且有着各种函数调用、数据转换等复杂的加工逻辑&#xff0c;如果通过传统的 ETL 工作模式&#xff0c;开发人员就不得不采用“盲人摸…

【H2O2|全栈】关于CSS(2)CSS基础(二)

目录 CSS基础知识 前言 准备工作 选择器的组合 盒模型 示例网页代码 后代选择器 亲代选择器 相邻兄弟选择器 后续兄弟选择器 多个元素选择器 通配符选择器 优先级 其他应用 伪类 锚链接的属性 列表的属性 list-style-type list-style-position list-style…

a√斗地主之顺子

题目描述 在斗地主扑克牌游戏中&#xff0c;扑克牌由小到大的顺序为:3,4,5.6,7.8,9,10,J,Q,K,A,2&#xff0c;玩家可以出的扑克牌阵型有:单张、对子、顺子、飞机、炸弹等。 其中顺子的出牌规则为:由至少5张由小到大连续递增的扑克牌组成&#xff0c;且不能包含2。 例如:(3.4.…

【JavaEE】IP协议 应用层协议

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【Java】登神长阶 史诗般的Java成神之路 &#x1f576;️一.IP地址 IP协议&#xff08;Internet Protocol&#xff09;是TCP/IP协议族中最核心的协议之一&#xff0c;它定义了数据包在网络中传输的标准…

快速使用react 全局状态管理工具--redux

redux Redux 是 JavaScript 应用中管理应用状态的工具&#xff0c;特别适用于复杂的、需要共享状态的中大型应用。Redux 的核心思想是将应用的所有状态存储在一个单一的、不可变的状态树&#xff08;state tree&#xff09;中&#xff0c;状态只能通过触发特定的 action 来更新…

测试工程师学历路径:从功能测试到测试开发

现在软件从业者越来越多&#xff0c;测试工程师的职位也几近饱和&#xff0c;想要获得竞争力还是要保持持续学习。基本学习路径可以从功能测试-自动化测试-测试开发工程师的路子来走。 功能测试工程师&#xff1a; 1、软件测试基本概念&#xff1a; 学习软件测试的定义、目的…

微信小程序开发——比较两个数字大小

在这里我们使用的工具是 需要自行安装和配置。 在微信小程序中比较两个数字大小有以下几种方式&#xff1a; 一、普通条件判断 在小程序的.js 文件中&#xff0c;先定义两个数字&#xff0c;如let num1 5; let num2 3;。通过if - else if - else语句&#xff0c;根据num1与…

【测试报告】博客系统

1.项目背景 在互联网高度发达的今天,越来越多的人开始学习编程,诞生了越来越多的程序员,但他们没有可以互相交流学习、分享经验的平台。本项目旨在为更多的程序员以及新手小白提供一个能够促进学习、共同进步&#xff0c;让小白也能成为大神的交流学习平台 1.1测试目标以及测试…

【数据结构】8——图3,十字链表,邻接多重表

数据结构8——图3&#xff0c;十字链表&#xff0c;邻接多重表 文章目录 数据结构8——图3&#xff0c;十字链表&#xff0c;邻接多重表前言一、十字链表结构例子 复杂例子 二、邻接多重表&#xff08;Adjacency Multilist&#xff09;例子 前言 除了之前的邻接矩阵和邻接表 …

Vue 第三方调用若依系统实现系统单点登录

应用场景 甲方现有平台系统拟集成我方新开发系统&#xff0c;实现单点登录功能&#xff0c;即用户登录主平台后&#xff0c;无需重复登录即可无缝访问新系统&#xff0c;提升用户体验与操作效率。 解决方案 实现代码 前端 Step:1 新建ssoLogin.vue页面 <template><d…