【C++学习笔记】【基础】4.string类

🍕阿i索 个人主页
《C语言专栏》 《C++专栏》
《数据结构专栏》 《LaTeX专栏》
《软件配置问题》 《Linux 专栏》
待更新...

前言:

C语言中的字符串:

在 C 语言中,字符串表现为以 '\0'(空字符)作为结束标志的字符序列(字符数组) 。尽管 C 标准库提供了 str 系列函数 (如 strcpy、strcat、strlen 等)用于字符串操作,但这类函数与字符串本身相互分离 ,既不符合面向对象 "数据与操作封装" 的设计思想,又因内存空间需完全由开发者手动管理,极易引发越界访问、缓冲区溢出、内存泄漏等安全问题。

而 C++ 标准库中的 std::string 类,是对 C 风格字符串的面向对象封装 :它基于 RAII(资源获取即初始化)机制实现内存的自动管理 ,彻底消除了手动分配 / 释放内存的负担;同时将字符串拼接、长度计算、查找等操作封装为类的成员函数 ,或通过重载运算符 (如 + 用于拼接、= 用于赋值)提供直观操作方式;此外,std::string 内置边界检查机制,并自动维护字符串结束标志,从根本上提升了字符串操作的安全性与易用性。

编码:

值和符号的映射关联集合。

ASCII 编码:

Unicode 编码:
百度百科

GBK编码:
百度百科


一、string类

了解

string 是 C++ 标准库提供的字符串类 ,定义在 <string> 头文件,属于 std 命名空间 。它是对 C 语言老式字符数组 char[]/char*面向对象封装 ,把字符串的底层内存管理、字符存储、各类操作都封装到类里,用类对象的方式管理字符串,不用手动处理 \0 结束符、不用自己控制内存开辟和释放。

1. string类对象的常见构造

构造函数 功能说明
string()(重点) 构造空字符串对象
string(const char* s)(重点) 用 C 风格字符串 char* 生成 string 对象
string(size_t n, char c) 创建包含 n 个相同字符 c 的字符串
string(const string& s)(重点) 拷贝构造,用已有字符串复制出新字符串
复制代码
#include<iostream>
#include<string>
using namespace std;

// string类对象的常见构造(前三个最常用)
void test_string1()
{
	string s1;					// 1.默认构造:空字符串,结果为 ""
	string s2("hello world");	// 2.带参构造(常量字符串本质是字符数组):完整拷贝常量字符串,结果为 "hello world"
	string s3(s2);	    		// 3.拷贝构造:完全复制s2的内容,结果为 "hello world"

	string s4(s2,0,5);			// 4.部分拷贝构造,从pos=0开始,取5个字符,结果为 "hello"
	string s5(s2,6,15);			// 部分拷贝构造,从pos=6开始,想取15个字符(超出字符串长度),实际取到字符串末尾,结果为 "world"。

	string s6(s2, 6);		    // 5.用前6个初始化:从pos=6开始,取到s2字符串末尾,结果为 "world"
	string s7("hello world", 6);// 取常量字符串的前6个字符:结果为 "hello "(末尾有空格)
	string s8(10, '*');			// 6.用10个*初始化

	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;
	cout << s4 << endl;
	cout << s5 << endl;
	cout << s6 << endl;
	cout << s7 << endl;
	cout << s8 << endl;

}

int main()
{
	test_string1();
	return 0;
}

二、string的使用

1. string类对象的常见接口函数

(1)容量操作

函数名称 功能说明
size() 返回字符串有效字符长度,不含'\0'(推荐使用)
length() 返回字符串有效字符长度,不含'\0'
capacity() 返回字符串当前分配的**总存储空间大小,**包含未使用的预留空间,不含'\0'
empty() 检测字符串是否为空串,空则返回 true,否则返回 false
clear() 清空所有有效字符,但不改变底层存储空间大小
reserve() 为字符串预留存储空间,不改变有效字符个数,仅调整容量
resize() 调整有效字符个数到 n,可指定填充字符

(2)访问及遍历操作

函数 / 用法 功能说明
operator[](重点) 按下标访问 / 修改字符,const 对象也可安全调用
begin() + end() begin()开头字符迭代器end()末尾字符的下一位迭代器,用于正向遍历
rbegin() + rend() 反向迭代器,从字符串末尾向前反向遍历
范围 for(C++11) 简洁自动遍历字符串每一个字符

(3)修改操作

函数名称 功能说明
push_back 在字符串末尾追加单个字符
append 在字符串末尾追加一整段字符串
operator+=(重点) 便捷追加字符 / 字符串,最常用
c_str()(重点) 将 string 转为C 语言 char * 格式字符串,兼容老接口
find + npos(重点) 从指定位置向后查找字符 / 子串 ,找不到返回string::npos
rfind 从指定位置向前反向查找字符 / 子串
substr 从指定下标 pos 开始,截取 n 个字符,返回子串
insert 在指定位置插入字符 / 字符串
replace 删除指定区间内容,替换为新字符串

(4)非成员全局函数

函数 / 运算符 功能说明
operator+ 拼接两个字符串生成新串;传值深拷贝,效率偏低,尽量少用
operator>>(重点) 输入重载,读取字符串(遇空格 / 换行停止)
operator<<(重点) 输出重载,直接打印字符串内容
getline(重点) 读取完整一整行输入(可保留空格)
关系运算符(> < == != >= <=)(重点) 按字典序,直接比较两个字符串大小

2. string类对象函数详解

容量操作:

(1)字符串长度获取

size()/length()

std::string 可通过下标 [] 遍历字符并直接修改,用法与字符数组一致,遍历范围通常结合 size()确定(size() 返回有效字符数,不含 '\0')。

std::string 提供size()和 length()两个成员函数获取长度,二者功能完全等价,均返回字符串中有效字符的个数 (不含结束符 '\0')。

复制代码
	//string的遍历 or 修改
	for (size_t i = 0; i < s1.size(); i++)
	{
		s1[i]++;
	}
	cout << s1 << endl;//输出:yfmmp!xpsme

	//返回长度(不包括'\0')
	cout << s1.size() << endl;//推荐
	cout << s1.length() << endl;

注意:

  1. size()length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()
  2. clear()只是将 string 中有效字符清空,不改变底层空间大小。
  3. resize(size_t n)resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
  4. reserve(size_t res_arg=0):为 string 预留空间,不改变有效元素个数,当reserve的参数小于 string 的底层空间总大小时,reserve不会改变容量大小。

(2)修改实际大小(size)

resize()

resize(n)用于直接改变容器的实际元素数量(size),会根据新大小新增或删除元素:

  • 若 n > 当前 size:在尾部新增元素,用默认值(0、空字符等)填充。
  • 若 n < 当前 size:删除尾部多余元素,直接截断保留前 n 个。

同时容器会根据需要自动扩容或缩容,其底层同样可能经历重新分配内存、拷贝数据、释放旧空间的过程,会导致原有迭代器失效,但会保证保留下来的元素内容与顺序不变。

复制代码
#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s3("hello world");
	cout << s3 << endl;

	//n<当前对象的size,相当于保留前n个,删除后面的数据
	s3.resize(5);
	cout << s3 << endl;

	//n>当前对象的size,插入数据
	s3.resize(10, 'x');
	cout << s3 << endl;

	return 0;
}

输出:
hello world
hello
helloxxxxx

(3)string扩容缩容

扩容与缩容的本质都是重新分配内存空间 :容器会先申请一块大小合适的新内存,再将原有数据完整拷贝到新地址,最后释放原来的旧内存。整个过程不会修改数据本身的值与顺序,但会改变数据的存储地址,导致原有的指针、迭代器全部失效。

<1>扩容reserve()

reserve (n) 的作用是向容器请求预留至少能容纳 n 个元素的内存空间,用于提前扩容,减少频繁动态分配带来的开销。

它只会影响容器的容量 capacity,不会改变实际元素个数 size。

并且 C++ 标准只保证当 n 大于当前容量时会扩容,不保证当 n 较小时会缩容。

  • 如果 n > 当前容量 编译器一定会扩容,把容量增大到至少 n。

  • 如果 n ≤ 当前容量 C++ 标准不保证缩容,编译器可以选择:

    • 什么都不做(容量不变)

    • 或者像 MSVC 那样,适当缩小到合适的内存块但这不是可靠功能,不能依赖它缩容。

      #include
      #include
      using namespace std;
      int main()
      {
      string s2;
      TestCapacity();
      s2.reserve(20); //提前预留20个空间
      cout << s2.size() << endl; //输出有效空间
      cout << s2.capacity() << endl;//输出真实空间

      复制代码
      s2.reserve(5);             //请求预留空间5个
      cout << s2.size() << endl;
      cout << s2.capacity() << endl;//n小于capacity,编译器可能会考虑少分配
      return 0;

      }

      输出:
      0
      31
      0
      15

<2>缩容shrink_to_fit()

shrink_to_fit()的本质是向容器请求缩容 :容器会重新分配一块恰好能容纳当前所有元素的更小内存 ,将原有数据拷贝到新空间后,真正释放掉原来那块较大的旧内存 ,最终使容量(capacity)与实际大小(size)保持一致,从而归还多余内存。典型的时间换空间策略。

复制代码
#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s2;
	TestCapacity();
	s2.reserve(20);            //提前预留20个空间
	cout << s2.size() << endl; //输出有效空间
	cout << s2.capacity() << endl;//输出真实空间

	s2.shrink_to_fit();        //缩容
	cout << s2.size() << endl;
	cout << s2.capacity() << endl;
	return 0;
}

输出:
0
31
0
15

修改操作:

(1)尾部追加函数

<1>push_back()

在字符串末尾插入 1 个字符, 只能是单个字符

复制代码
string s = "abc";
s.push_back('d');   // s 变成 "abcd"
<2>append()

在字符串末尾追加字符串, 参数可以是字符串、字符数组、指定长度的字符序列

复制代码
string s = "abc";
s.append("def");    // s 变成 "abcdef"
<3>operator+=/operator+

string 类的重载赋值运算符函数 ,功能同样是在字符串尾部追加内容。它既可以拼接字符串 ,也可以追加单个字符 ,用法更直观简洁,效果与 append 相近,是日常代码中最常用的字符串拼接方式。

复制代码
string s = "hello";
s += " world";   // 追加字符串,s 变为 "hello world"
s += '!';        // 追加单个字符,s 变为 "hello world!"

operator+是std下的全局重载函数,用来拼接两个字符串,生成一个全新字符串, 原字符串不会被修改。

与operator+=区别:

  • operator+ 是全局函数,左右均可拼接;
  • operator+=是成员函数,只能在自身后追加,左边参数必须是 string 对象本身。

(2)尾删函数

pop_back()是 string 类的尾部删除成员函数,用于删除字符串最后一个字符,执行后字符串长度减一,无参数也无返回值,直接修改原字符串内容。示例:

复制代码
string s = "hello";
s.pop_back();   // s 变为 "hell"

(3)c_str()

把 C++ 的string转为 C 语言const char*字符串,自带\0,用来兼容 C 语言接口(如 fopen)。示例

复制代码
// 用法1:兼容文件打开函数 fopen
#include <iostream>
#include <string>
#include <cstdio>
using namespace std;

int main()
{
	string filename("Test.cpp");
	// fopen 只接受 const char*,必须用 c_str() 转换
	FILE* fout = fopen(filename.c_str(), "r");
	if (fout)
	{
		cout << "打开文件成功!" << endl;
	}
	return 0;
}

// 用法2:兼容需要 C 格式字符串的接口
int main()
{
	string str("hello world");
	// 调用需要 C 格式字符串的函数时,使用 c_str()
	send(str.c_str()...);
	return 0;
}

(4)substr()

截取子串,从起始下标 开始,拷贝指定长度的字符,生成新字符串。substr只传一个参数时,从指定下标开始,默认一直截取到字符串末尾。示例

复制代码
// 截取文件后缀名
int main()
{
	string filename("Test.cpp");
	// 从下标 4 开始,截取 4 个字符
	string suffix = filename.substr(4, 4);
	// 结果:suffix = ".cpp"

    // 只给起始下标4,默认从4截取到最后, 
    string suffix = filename.substr(4);  
    // 结果:suffix = ".cpp"
	return 0;
}

(5)find()

find()用于在字符串中定位目标内容,支持从指定下标(默认从 0 开始)向后查找,返回第一个匹配位置的索引。若找不到目标,会返回常量 string::npos(通常为 - 1),这是判断查找是否成功的关键标志。示例:

复制代码
string s = "hello C++ hello world";
size_t pos1 = s.find("hello");   // 找子串,返回首次出现的下标0
size_t pos2 = s.find('h', 1);    // 从下标1开始找字符'h',返回下标10
if (pos2 != string::npos) {
    cout << "找到位置:" << pos2 << endl;
} else {
    cout << "未找到" << endl;
}

(6)rfind()

从字符串末尾反向查找 字符 / 子串,返回最后一次出现的下标。示例:

复制代码
int main()
{
    string filename("test.v1.2.3.tar.gz");
    // 从后往前找最后一个 .
    size_t pos = filename.rfind('.');
    string suffix = filename.substr(pos);
    // 结果:suffix = ".gz"
}

find()与substr()配合:从完整 URL 拆解出协议、域名、路径三部分:

复制代码
#include <iostream>
#include <string>
using namespace std;

int main()
{
	// 待拆分的URL网址
	string url = "https://cplusplus.com/reference/string/string/rfind/";

	// 查找第一个冒号 : 的位置,用于分割协议
	size_t i1 = url.find(':');

	// 判断是否找到
	if (i1 != string::npos)
	{
		// 截取从0到i1的子串,提取协议名称(https)
		// 左闭右开:结束下标 - 起始下标 = 字符个数
		// 左闭右闭:结束下标 - 起始下标 会少1个字符
		string protocol = url.substr(0, i1);
		cout << "协议:" << protocol << endl;

		// 从 i1+3 位置开始查找第二个冒号(跳过://)
		size_t i2 = url.find(':', i1 + 3);
		if (i2 != string::npos)
		{
			// 截取域名部分:从 i1+3 开始,长度为 i2 - (i1+3)
			string domain = url.substr(i1 + 3, i2 - (i1 + 3));
			cout << "域名:" << domain << endl;

			// 截取从 i2+1 到末尾的所有内容,即资源路径
			string uri = url.substr(i2 + 1);
			cout << "路径:" << uri << endl;
		}
	}
	return 0;
}

注意:

左闭右开区间,是包含起始下标、不包含结束下标,用结束下标减去起始下标,得数就是要截取的字符数量;左闭右闭区间,是既包含起始下标、也包含结束下标,这时结束下标减起始下标的结果比实际字符数少一个,算截取长度时需要额外加1。

访问及遍历操作:

(1)运算符重载 operator 函数

C++ 标准库中的 std::string 提供了两种字符访问方式:operator 和at() ,二者都能返回指定位置字符的引用以支持读写修改。

  • operator[] 是最直观的访问方式,用法与数组下标完全一致,无需额外检查,因此执行效率更高。当访问的下标超出字符串有效范围时,会触发未定义行为,部分编译器Debug模式下会有严格的断言处理。

  • at() 函数在功能上与 operator[] 等价,但会主动进行边界检查,一旦下标越界就会抛异常,需要通过 try-catch 结构捕获处理。

    #include
    #include
    using namespace std;

    // 运算符重载 std::string::operator[],返回pos位置上的字符,引用返回,可以修改返回值
    // 可以像使用数组一样使用string
    void test_string2()
    {
    string s1("hello world");

    复制代码
      // 把第0个位置的字符改为x
      s1[0] = 'x';
      cout << s1 << endl;
    
      // []越界访问,未定义行为(Debug可能断言,Release无提示)
      s1[12];
    
      // at与[]类似,只是越界会抛异常
      s1.at(12);

    }

    int main()
    {
    // at越界抛异常,需要捕获
    try
    {
    test_string2();
    }
    catch (const exception& e)
    {
    cout << e.what() << endl;
    }
    return 0;
    }

在实际代码中,operator[] 可以直接用于修改字符,比如 string s1("hello world"); s1[0] = 'x'; 会将字符串首字符修改为 'x',而 s1[12]; 这样的越界访问则不会有任何异常提示,直接产生未定义行为;at() 的调用形式为 s1.at(12);,越界时会抛出异常,必须在 main 函数中通过 try 块包裹调用逻辑,并在 catch 块中捕获 const exception& 类型的异常,通过 e.what() 打印异常信息,避免程序直接终止。

(2)比较大小函数

relational operators

string 的关系运算符是专门用于比较两个 string 对象大小 / 相等关系非成员全局重载函数 ,支持 ==、!=、<、<=、>、>=,按字符串 ** 字典序(ASCII 码)** 逐字符比较,直接用运算符比较即可,无需调用成员函数。示例

复制代码
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s1 = "apple";
    string s2 = "banana";
    
    // 直接使用关系运算符比较两个string
    cout << (s1 == s2) << endl;  // 是否相等
    cout << (s1 != s2) << endl;  // 是否不等
    cout << (s1 < s2)  << endl;  // s1字典序是否更小
    cout << (s1 > s2)  << endl;  // s1字典序是否更大
    return 0;
}

(3)读取整行函数

getline 是用来读取一整行带空格的字符串,从输入流中逐个字符读取直到遇到换行符为止,会自动遍历整行字符、存入 string 对象,只读取访问字符内容,不会修改原有字符串,是整行读取遍历访问的常用函数。示例:

复制代码
#include <iostream>
#include <string>
using namespace std;

int main()
{
    string s;
    getline(cin, s);  // 读取整行(含空格)
    cout << s << endl;
    return 0;
}

(4) 迭代器

iterator 迭代器

C++ std::string 提供迭代器(iterator)作为通用的遍历与修改方式。迭代器是 std::string 类域内的类型(string::iterator),行为上与指针高度相似,可用于精准访问和修改字符串中的字符。迭代器作为 C++ 容器通用遍历方式,比下标遍历更适配 sort、find 等 STL 算法。

使用时需通过 s1.begin()获取指向字符串第一个字符的迭代器,通过s1.end()获取指向最后一个字符下一个位置的尾后迭代器(尾后迭代器不可解引用),遍历范围以 it1 != s1.end() 为边界(迭代器仅支持 或者!=比较,不支持 < 等关系运算符)。begin()和end()类似于[ )。

复制代码
#include<iostream>
#include<string>
using namespace std;

//iterator正向迭代器(遍历从前到后)
void test1()
{
	string s1("hello world");
	//在string这个类域里面有一个iterator的迭代器类型
	//迭代器从行为上像指针一样
	//begin获取第一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
	string::iterator it1 = s1.begin();
	while (it1 != s1.end())
	{
		(*it1)--;//修改值,将it1指向的值--
		cout << *it1 << " ";//想要访问迭代器指向位置的字符,解引用就可以
		++it1;//指向下一个迭代器
	}
	string::const_iterator it2 = s1.begin();//const迭代器是单独的一个类型,指向的内容不能修改
	//*it2--;//不能修改
}
int main()
{
	test1();	//test1();//输出:g d k k n  v n q k c
	return 0;
}

实际使用时,先定义string::iterator it1 = s1.begin();让迭代器指向字符串起始位置,再通过while循环遍历(循环条件为it1 != s1.end()):循环内解引用*it1可访问 / 修改字符(如(*it1)--使字符 ASCII 码减 1),输出字符后通过前置自增++it1移动迭代器(效率高于it1++)。对 "hello world" 执行该操作会输出g d k k n ! v n q l d 。

string::const_iterator只能读不能修改。

reverse_iterator 反向迭代器

C++ std::string 提供反向迭代器(reverse_iterator)实现字符串从后到前的遍历,用于逆序访问字符,反向迭代器通过 ribegin()(指向最后一个字符)和rend()(指向第一个字符前)确定逆序遍历范围。其中 const_reverse_iterator 是只读反向迭代器,适用于仅读取字符不修改的场景。

复制代码
//反向迭代器(遍历从后到前)
void test2()
{
	string s1("hello world");
	string::const_reverse_iterator it1 = s1.rbegin();
	while (it1 != s1.rend())
	{
		cout << *it1 << " ";//想要访问迭代器指向位置的字符,解引用就可以
		++it1;//指向下一个迭代器
	}
}
int main()
{
	test2();//输出:d l r o w   o l l e h
	return 0;
}

迭代器特点:

1.提供统一的方式遍历修改容器。

2.算法可以泛型化(模板),算法在模板中借助迭代器处理容器的数据。

(5) 范围for和auto

范围 for 是 C++11 新增的遍历方式,可以自动取容器数据赋值,自动迭代++,自动判断结束。结合 auto 可快速遍历容器的所有元素。

auto 是 C++11 引入的类型推导关键字,可根据变量的初始化表达式自动推导变量类型,可以简化冗长的类型声明,尤其适配迭代器这类复杂类型。

  • 简化类型声明:无论是迭代器这类前缀冗长的复杂类型,还是 int 等基础数据类型,auto 均可根据初始化值自动推导变量具体类型,替代繁琐的完整类型声明。

  • 用 auto 声明指针时,auto p = & 变量 与 auto* p = & 变量 效果一致,均能推导为指针类型;带 * 的写法(auto*)要求右侧必须传入指针类型的值,语义上更明确表达 "指针" 意图。

  • auto 默认不会推导为引用类型,即使初始化值是引用,auto 也会推导为对应的基础类型;若需让 auto 推导为引用类型,必须显式在 auto 后加 &。

复制代码
//auto通过初始化表达式值的类型制动推荐对象类型
//迭代器类型前缀太长就可以用auto代替,比如:string::iterator it1 = s1.begin(); -> auto it1=s1.begin();
void test3()
{
	//迭代器类型前缀太长就可以用auto代替
	// 比如:string::iterator it1 = s1.begin(); -> auto it1=s1.begin();
	string s1("hello world");
	auto it1 = s1.begin();

	int i = 0;
	auto j = i;
	auto k = 12;
	//用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
	auto p1 = &i;//不加*,也可以推导出是指针
	auto* p2 = &i;//加上*,则右边一定传指针

	//引用
	int& r1 = i;  //r1是i的别名
	auto r2 = r1; //r2推导出来是int类型,不会是引用
	auto& r3 = r1;//r3是int& 引用
}

//范围for
//自动取容器数据赋值,自动迭代++,自动判断结束
//数组和支持迭代器的容器,都可以用范围for
void test4()
{
	string s1("hello world");
	for (auto ch : s1)
	{
		cout << ch << ' ';
	}
	for (char ch : s1)
	{
		cout << ch << ' ';
	}
	//范围for是对容器内数据进行拷贝,如果想改变数据,要使用引用
	for (char& ch : s1)
	{
		ch-=1;
	}
	//如果数据比较大,不想拷贝,就用const引用
	for (const char& ch : s1)
	{
		cout << ch << ' ';
	}
}
int main()
{
	test3();
	test4();
	return 0;
}

其他函数(了解)

(1)insert()

insert()是 string 类的插入成员函数,可在字符串指定位置插入字符、字符串等内容,不覆盖原有数据,只向后挪动字符。示例:

复制代码
    string s = "abcde";

    // 1. 指定位置插入字符串
    s.insert(2, "XYZ");
    cout << s << endl;  // abXYZcde

    // 2. 指定位置插入 n 个相同字符(从下标为3的位置开始插入2个)
    s.insert(3, 2, '8');
    cout << s << endl;  // abX88YZcde

    // 3. 插入另一个字符串的子串(从s的下标1位置开始,插入s2中从下标2开始向后长度为3的子串)
    string s2 = "123456";
    s.insert(1, s2, 2, 3);
    cout << s << endl;  // a345bX88YZcde
//  insert(pos, 源字符串, 源串起始索引, 插入长度)

    // 4. 迭代器位置插入单个字符(在下标1的位置插入9)
    s.insert(s.begin() + 1, '9');
    cout << s << endl;  // a9345bX88YZcde
(2)erase()

erase()是 string 类的删除成员函数,可删除指定位置、指定长度的字符,也可删除单个字符或区间字符。示例:

复制代码
string s = "abcde";
s.erase(1, 2);      // 从下标1删2个字符,s变为"ade"
(3)assign()

assign()是 string 的赋值成员函数 ,用于重新赋值字符串 ,直接覆盖原有内容,相当于 =。示例:

复制代码
string s;
s.assign("hello");    // 直接赋值
s.assign("abcde", 3); // 取前3个字符:abc
s.assign(5, 'x');     // 5个x:xxxxx
(4)replace()

replace(位置, 长度, 新内容):从字符串的指定下标开始,删掉对应长度的字符,然后在该位置换上新内容,一步完成删除 + 插入。示例:

复制代码
string s = "hello world";
s.replace(6, 5, "C++"); // 从下标6开始删5个字符,替换成"C++"
// 结果:hello C++
(5) find_first_of

从前往后查找字符串中任意一个字符第一次出现的位置。示例:

复制代码
string s = "abc123abc";
int pos = s.find_first_of("123"); // 找到 '1',pos = 3
(6)find_last_of

从后往前查找字符串中任意一个字符最后一次出现的位置。示例:

复制代码
string s = "abc123abc";
int pos = s.find_last_of("abc"); // 找到最后一个 'c',pos = 8