【STL专题】深入探索C++之std::string:不止于字符串【万字详解】

news2024/11/23 9:50:05

欢迎来到CILMY23的博客

🏆本篇主题为:深入探索C++之std::string:不止于字符串

🏆个人主页:CILMY23-CSDN博客

🏆系列专栏:Python | C++ | C语言 | 数据结构与算法 | 贪心算法 | Linux

🏆感谢观看,支持的可以给个一键三连,点赞关注+收藏。


✨写在前头: 

了解完模板和STL后,我们要开始研究std命名空间中的string,作为我们STL学习的开端,string对我们重新认识C语言中的字符串有很大意义。在上文中我们说过容器(Containers),容器是用来存储数据的对象,如数组、链表、树结构等。STL提供了多种类型的容器,本期我们将了解string类,虽然string并没有被归为容器,但它也和容器类似。


string 

一、string的前瞻

1.1 C语言中的字符串

C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数, 但是这些库函数与字符串是分离开的,不太符合OOP(面向对象编程)的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问

 我写C语言字符串系列篇的时候还是深有体会的,稍不留神就越界访问导致出错,有时候还难排查出来。

1.2 string的基本概念

string 类是 C++ 标准库中的一部分,它提供了对字符序列的封装,使得字符序列的操作变得既简单又安全。

☄️☄️文档阅读 

我们来看看文档是怎么说的:文档的阅读网站我放在了文章末尾,在这里我分了两部分的文档基本阅读

🍀🍀图一: 

 🍀🍀图二:

☄️☄️信息翻译
  1. 字符串是表示字符序列的类
  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作 单字节字符字符串的设计特性。
  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信 息,请参阅basic_string)。
  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. 不能操作多字节或者变长字符的序列。

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

二、string类的常用接口

 💫String 类的默认成员函数

string的默认成员函数我们在这里主要看以下三部分: 

🍃构造函数(⭐)

 进入构造函数的文档界面,我们看到有七个构造函数,其中重点掌握的,也是最常用的,我在图片中已经标出来了。剩下的了解就差不多了。

在这里我们要了解第三个,前面两个很容易理解,但是第三个是什么呢?npos又是什么呢?

我们来看声明和定义,文档在底下给了我们的定义 

string (const string& str, size_t pos, size_t len = npos) 

 解析我在图片中标出来了,大家可以研究研究。

 substring constructor是指字串构造。

现在主要的问题是 npos 是个什么东西? 文档给我们提供了链接,我们可以点击查看(链接)

 

这里一看我们就知道了,原来 npos 是 size_t (无符号整数)的最大值,这就涉及原码反码和补码了(不懂的小伙伴可以看看链接)。 

-1 的补码是32个1,也就是2的32次方-1,但是string构造函数并不会开到这么多空间,所以就只会判定拷贝直到str的末尾。

🍭🍭实际操作 

写了这么多,我们来实际操作一下。在vs 2019上对这几个构造函数进行使用:记得包含头文件<string>

string s0;
string s1("CILMY23");
string s2(s1,2,3);
string s3(s2);//拷贝构造
string s4(s1, 3, string::npos);//从第三个位置开始,拷贝到str字符串末尾
string s5(10, '*');
string s6("CILMY23", 2);

结果如下: 

 

🍃析构函数

对析构函数我们不必关注太多,稍微看看就行

在学习的时候,我们知道析构函数的功能即可 

 🍃赋值运算符重载

在这里赋值运算符重载主要给了三种方式 

 这三种方式的实操如下:

🍭🍭实际操作 

string s7;

s7 = s2; //将s2拷贝给s7
s7 = "CILNY23";//将常量字符串赋值给s7
s7 = '*';//将单字符*赋值给s7

 结果如下:

 赋值运算符重载用的最多的还是前两种,一般第三种还是挺少用的。

 💫String 类的容量操作

string 的容量操作一共有以下操作:

 我们重点学习:size,empty,clear,reserve,resize

 🎈size(⭐),length,capacity,max_size

在string类中,sizelength是一样的,都是返回字符串的长度。,它们表示的是字符串中,有效字符的个数,而capacity表示的是容量,max_size,它返回 string 类型对象最多包含的字符数。也就是string类型支持的最大字符数,超过这个数,将无法支持,编译器会拋出 length_error 异常。

 我们知道在字符串的末尾是有\0的,在这里是不计入size和length的。

为什么这里会相同呢?

 这就不得不提及STL和String的先后问题了,其实是String比STL先出现的,所以String在刚刚的网站中我们是在Container中是找不到String的,但是它跟容器差不多,所以就增加了一个size接口,起初最先我们使用的是lengthsize()length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。 

说完了sizelength,我们接下来看看capacity

 

capacity返回的是开辟空间的大小,它是一个const成员函数,this指针指向的内容是不可以改变的 ,它和size是不一样的,有可能等于size,也可能比size还多

我们可以看一下在vs上它是如何扩容的, 

size_t sz = s1.capacity();
int n;
for (n = 0; n < 1000; n++)
{
	s1.push_back('*');

	if (sz != s1.capacity())
	{
		sz = s1.capacity();
		cout << "capacity changed->" << s1.capacity() << endl;
	}
		
}

结果如下:

我们可以看到它的扩容机制大概是按1.5倍来扩容的 

而g++底下的扩容机制是按照两倍扩容的。 

所以说具体的这些细节,是不确定的。 

 🍭🍭实际操作 

string s1("hello CILMY23");

cout << s1.size() << endl;
cout << s1.length() << endl;
cout << s1.capacity() << endl;
cout << s1.max_size() << endl;
cout << s1.npos << endl;

 结果如下:

但是我这里max_size可能是受到了系统或者编译器的限制,在我查阅很多文档后,都说它比npos还小1个,这里得考虑字符'\0'。所以最大长度是不确定的,一般给的是当前平台最大值。

 🎈empty

 文档介绍:这是一个负责测试string是否为空表,如果是空就返回true,否则返回false

这个函数较为简单就不进行实操演示了 

 🎈clear(⭐)

clear函数主要是删除这个string中包含的字符,让它变成一个空表,我们知道空表的判定,是长度length为0,所以这里的clear是不会动capacity容量的,是只会清除有效字符。

 clear() 被调用,string 对象仍然存在,它的内存没有被释放,但其内容被清空了。

 🎈reserve(⭐)

它的作用是请求改变容器的容量。具体来说,它会预分配足够多的内存空间以便存储至少指定数量的元素,这个数量由函数的参数指定。这意味着使用 reserve 可以减少由于添加新元素导致的多次内存重新分配操作,从而提高程序性能。

需要注意的是,reserve 函数只改变容器的容量(即可存储的元素数量,而不必进行再分配),但不改变容器的大小(即实际存储的元素数量)。也就是说,即使调用了 reserve,size() 和 length() 函数的返回值也不会因此改变;reserve 也不会影响容器中已有元素的内容。

  🍭🍭实际操作 

void Teststring2()
{
	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;
}

// 利用reserve提高插入数据的效率,避免增容带来的开销

 结果如下:

但是你开100的空间,编译器不一定会只给你一百个空间,在g++当中,确实是开了一百空间,如果看会不会缩容,就是缩小空间的话,vs是不会缩小的。也就是reserve是比capacity大才会扩容

 🎈resize(⭐)

reserve会比resize用的更加广泛些,让我们来看看resize吧

resize重载了两个函数: 

resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字
符个数增多时;resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的
元素空间。

注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。 

 🍭🍭实际操作 

void Teststring1()
{
	string s("hello, world");
	cout << s.size() << endl;
	cout << s.length() << 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;
}

 结果如下:

 🎈shrink_to_fit(⭐) 

它说的是减少字符串所占用的空间以匹配它的实际内容大小会请求字符串对象减少其容量,以适应它的实际大小,释放未使用的内存。

 🍭🍭实际操作  

假设你有一个字符串 s,如果你在对它进行了多次修改之后,希望确保它不占用额外的空间,你可以调用shrink_to_fit

std::string s = "aaaaaa";

// ... 对字符串s进行了一些操作,可能使其大小发生了变化

s.shrink_to_fit(); // 释放未使用的内存

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

类对象的访问和遍历操作主要有以下两个部分,重点部分是operator[],和迭代器(也就是begin,end)。

⛵⛵类对象的访问
🌠opertor[](⭐) 

运算符重载后的[],支持了两个版本,它的作用是返回pos所在字符串中指向的位置,如果pos和字符串的长度相等,那它就会返回一个‘\0’。

这两个版本主要的差别不是很大,前一个的权限是可读可写,后一个的权限是只读,它们构成了函数重载,是因为this指针被const修饰了。

  🍭🍭实际操作  

我们进入实际操作更能理解重载后的[], 它采用的是 【下标】的形式

void Teststring3()
{
	string s1("hello cilmy");
	const string s2("Hello cilmy");
	cout << s1 << " " << s2 << endl;
	cout << s1[0] << " " << s2[0] << endl;

	s1[0] = 'H';
	cout << s1 << endl;

	// s2[0] = 'h';   代码编译失败,因为const类型对象不能修改
}

 我们使用它就像使用数组一样方便快捷,并且可以特定位置返回修改对应位置的字符。

结果:

🌠at

 在平常使用中,我们更经常使用 [] ,但 at 的功能和它是类似的,同样都是重载了两个对应的函数,也是返回对应下标的字符,区别不同的点在于,at在访问字符串时会执行范围检查。如果尝试访问的位置超出了字符串的当前长度,它会抛出一个异常,从而避免了潜在的未定义行为。

   🍭🍭实际操作  

at的实际操作如下所示: 采用 at(下标) 的形式

//at 和 [] 的区别
void test4()
{
	string str = "hello";

	try {
		// 安全的访问方法,但可能抛出异常
		char ch1 = str.at(10);
	}
	catch (const std::out_of_range& e) {
		// 处理越界访问
		cout << "越界访问异常: " << e.what() << '\n';
	}

	// 不安全的访问方法,但不会抛出异常,在越界时行为未定义
	char ch2 = str[10];  
}
🌠back和front

back和front看起来是C++11才有的,目前大部分编译器都支持这个版本,它实际上是为了规范才提供的,要跟其他容器保持一致,back和front的返回无非就是字符串的最后一个字符和第一个字符。在这里就不做展开讲解了,看看文档就好啦。

⛵⛵string类的遍历

 string类的遍历支持三种方式,一种是迭代器,一种是重载后的[],一种是范围for,但范围for的本质还是迭代器实现的,而且迭代器的遍历是主流的一种方式,更经常使用迭代器,此外我们还会涉及到逆置输出字符串,那就让我们进入实操看看吧。

 🍭🍭实际操作  

 🌠[](⭐) 

 假设我们有个字符串s,采用 size() + [] 的方式进行遍历字符串s,但是注意,这个可不同于C语言中的 [] ,那样是等价于 *()

string s("hello CILMY23");
//[]的遍历
for (int i = 0; i < s.size(); i++)
{
	cout << s[i] << " ";
	cout << s.operator[](i) << " ";
}
cout << endl;

//修改
for (int i = 0; i < s.size(); i++)
{
	s[i] += 1;
	cout << s[i] << " ";
}

cout << endl;

//如果你不想修改,那就用const版本

const string s2("hello CILMY23");
for (int i = 0; i < s.size(); i++)
{
	//无法修改
	s2[i] += 1;

	cout << s2[i] << " ";
}

cout << endl;

}
 🌠迭代器(⭐) 

 迭代器它的行为有点类似指针

string str("hello CILMY23");
//第二个遍历方式,迭代器+begin()+end()
string::iterator it = str.begin();

while (it != str.end())
{
	cout << (*it) << " ";
	++it;
}

cout << endl;

 在这里我们就要补充迭代器的begin位置和end位置了,begin是h,end是‘\0’

同样它也能修改字符内容 

//修改字符
string::iterator it1 = str.begin();
while (it1 != str.end())
{
	*it1 -= 1;
	cout << (*it1) << " ";
	++it1;
}

cout << endl;

 从使用的角度上,[]确实是方便,但是更通用的还是迭代器,迭代器是容器的核心访问,它能访问更多的情况。

 🌠 范围for

 它的本质其实也是一个迭代器

string str1("hello CILMY23");

for (auto e : str1)
{
	cout << e << " ";
}

cout << endl;

 推荐在正向遍历的时候使用,反正也是简洁使用。

💫string类对象的修改操作 

类对象的修改操作一共有以下这么多:

1️⃣ push_back

push_back  的功能是在尾部插入一个字符。但是要注意的是,它在设计上,是真的只能插入一个字符

我们来看实际操作:

string s("hello");

s.push_back('C');
s.push_back('I');
s.push_back('L');
s.push_back('M');
s.push_back('Y');

cout << s << endl;
 2️⃣ append

我们可以看到append一共重载了六个, append 是 string 的一个成员函数,用于向字符串的末尾添加内容。

我们来看实际操作下的append

void test7()
{
    // 创建一个string对象s,并初始化为"hello"
    string s("hello");

    // 向s追加字符串"CILMY23",s变为"helloCILMY23"
    s.append("CILMY23");
    cout << s << endl; // 输出:helloCILMY23

    // 向s追加了10个'*'字符,s变为"helloCILMY23**********"
    s.append(10,'*');
    cout << s << endl; // 输出:helloCILMY23**********

    // 创建另一个空的string对象s1
    string s1;
    // 向s1追加字符串"xx hello CILMY23 xx",s1变为"xx hello CILMY23 xx"
    s1.append("xx hello CILMY23 xx");

    // std::string的begin和end函数返回指向字符串第一个字符和尾后字符的迭代器,respectively
    // 这里,通过迭代器向s追加s1的部分内容:"x hello CILMY23 x"
    // ++s1.begin() 移动到s1的第二个字符,--s1.end()移动到s1的最后一个有效字符
    s.append(++s1.begin(), --s1.end());
    // 输出s此时的内容:helloCILMY23**********x hello CILMY23 x
    cout << s << endl;
    // 注意:第三次追加实际上省略了s1字符串的首尾两个'x'
}

 当然我们在实际操作中,更喜欢用 重载后的+=

3️⃣ operator+=(⭐)

根据以下的文档说明,我们可以直接在字符串末尾添加一个字符串,或者单个字符,又或者是另一个字符对象。 

  🍭🍭实际操作  

void test8()
{
	string s("hello");
	string s1("xxxxxx");
	s += ' ';
	s += "CILMY23";
	s += s1;

	cout << s << endl;
}

 +=使用起来简单方便,清楚明白,在使用中,我个人还是更偏向使用+=的。

4️⃣ insert

insert这个函数,重载形式也蛮多的,在C++中,std::string 类同样提供了 insert 函数来在字符串中的指定位置插入内容。与 append 相比,insert 提供了更高的灵活性,它允许你在字符串的任何位置添加字符或字符串。

   ℹ️ℹ️具体介绍

insert(size_type pos, const std::string& str):
//在当前字符串的 pos 位置插入字符串 str。

insert(size_type pos, const std::string& str, size_type subpos, size_type sublen):
//将字符串 str 从索引 subpos 开始的 sublen 长度的子字符串插入到当前字符串的 pos 位置。

insert(size_type pos, const char* s):
//将字符串 s 插入到当前字符串的 pos 位置。

insert(size_type pos, const char* s, size_type n):
//将字符串 s 的前 n 个字符插入到当前字符串的 pos 位置。

insert(size_type pos, size_type n, char c):
//在当前字符串的 pos 位置插入 n 次字符 c。

insert(iterator p, char c):
//在迭代器 p 指定的位置插入一个字符 c。

insert(iterator p, size_type n, char c):
//在迭代器 p 指定的位置插入 n 次字符 c。

insert(iterator p, InputIterator first, InputIterator last):
//在迭代器 p 指定的位置插入另一个字符串或字符数组,该字符串由 first 到 last 指定的范围决定。

 这个主要作为了解即可,不需要过于在意去记忆。insert最经常用的还是insert(size_type pos, const char* s),切记不要多用,因为它的底层是挪动数据,这样会造成代码效率低下。

5️⃣ assign

 assign:v. 分派,布置(工作、任务);分配(某物);指派,派遣;确定(价值、功能、时间、地点);转让(财产、权利)

assign的意思如上,在C++标准库中,std::string 的 assign 函数用于给字符串赋新值。这个函数替换字符串的当前内容,可以从多种不同类型的源赋值,例如从另一个 string、从字符串,或是直接从字符数组来赋值。

    ℹ️ℹ️具体介绍

assign(const std::string& str):
//用字符串 str 的内容替换当前字符串的内容。

assign(const std::string& str, size_type subpos, size_type sublen):
//用 str 的一个子串替换当前字符串的内容,这个子串从 subpos 开始,长度为 sublen。

assign(const char* s):
//用字符串 s 的内容替换当前字符串的内容。

assign(const char* s, size_type n):
//用字符串 s 的前 n 个字符替换当前字符串的内容。

assign(size_type n, char c):
//用 n 个重复的字符 c 替换当前字符串的内容。

assign(InputIterator first, InputIterator last):
//用两个迭代器 first 和 last 指定范围内的字符替换当前字符串的内容。

 大概就介绍到这里,erase 用的还是比较多的。

6️⃣ erase (⭐)

 erase还是挺好用的,erase 函数用来删除字符串中的一部分内容,是一个非常有用的成员函数,允许多种不同的用法以适应不同的需求。

 ℹ️ℹ️具体操作

string str = "Hello, World!";

// 用法1: 删除从位置3开始的5个字符
str.erase(3, 5);
cout << str << '\n'; // 输出: Hel, World!

// 重置字符串
str = "Hello, World!";

// 用法2: 删除位置为0的字符
auto it = str.erase(str.begin());
cout << str << '\n'; // 输出: ello, World!
cout << *it << '\n'; // 输出e,即下一个字符

// 重置字符串
str = "Hello, World!";

// 用法3: 删除第3个字符到第6个字符之间的所有字符,包括第3个,不包括第6个
str.erase(str.begin() + 2, str.begin() + 6);
cout << str << '\n';  // 输出: He World!

当然它的底层也是通过挪动数据实现的,能不用尽量不用。

7️⃣replace

 在C++中,string 的 replace 成员函数允许我们替换字符串中的某部分内容。这个函数非常灵活,提供了多种重载版本。

  ℹ️ℹ️具体操作

string str = "I like C++ programming.";

// 示例 1: 使用另一个字符串替换
str.replace(7, 3, "Python");
cout << str << '\n';  // 输出: I like Python programming.

// 示例 2: 使用迭代器范围和字符串替换
str.replace(str.begin(), str.begin() + 6, "He loves");
cout << str << '\n';  // 输出: He loves Python programming.

// 示例 3: 使用C风格字符串替换部分内容
str.replace(8, 6, "Java", 4);
cout << str << '\n';  // 输出: He loves Java programming.

// 示例 4: 用字符替换一部分内容
str.replace(str.begin(), str.begin() + 8, 4, 'X');
cout << str << '\n';  // 输出: XXXX Java programming.

 这里就不多做演示了,感兴趣的读者可以自己实验一下

💫string类对象的字符串操作  

💧find(⭐)

 如果我们想覆盖字符串,删除,替换,需要找到一个位置的时候,find()就显得极其重要。find 函数用于在字符串内查找子串或字符的第一个出现位置如果找到了匹配项,它就返回匹配项的下标;如果没有找到,它则返回 npos,这是一个特别定义的常量,表示不存在的位置。

find(const std::string& str, size_type pos = 0) const:
从位置 pos 开始搜索字符串 str。
find(const char* s, size_type pos = 0) const:
从位置 pos 开始搜索以空字符结尾的字符串 s。
find(const char* s, size_type pos, size_type n) const:
从位置 pos 开始,搜索字符串 s 的前 n 个字符。
find(char c, size_type pos = 0) const:
从位置 pos 开始,搜索字符 c。

   ℹ️ℹ️具体操作

string str = "We are looking for the term in this string.";

// 查找子串
size_t found = str.find("term");
if (found != std::string::npos)
	cout << "Found 'term' at index: " << found << '\n';

// 查找C风格的字符串
found = str.find("the term");
if (found != std::string::npos)
	cout << "Found 'the term' at index: " << found << '\n';

// 查找单个字符
found = str.find('t');
if (found != std::string::npos)
	cout << "Found 't' at index: " << found << '\n';

// 从某一位置后开始查找
found = str.find('i', 10);
if (found != std::string::npos)
	cout << "Found 'i' after index 10 at index: " << found << '\n';

// 查找不存在的字符串
found = str.find("nonexistent");
if (found == std::string::npos)
	cout << "Did not find 'nonexistent'.\n";
💧rfind 

在C++中,string 类提供了 rfind 函数用于从字符串的末尾开始向前搜索子串或者字符的最后一次出现的位置。如果找到了匹配项,它返回该匹配项的起始下标,如果没有找到,它返回npos。

rfind(const std::string& str, size_type pos = npos) const:
从位置 pos 开始向前搜索字符串 str 的最后一次出现。
rfind(const char* s, size_type pos = npos) const:
从位置 pos 开始向前搜索字符串 s 的最后一次出现。
rfind(const char* s, size_type pos, size_type n) const:
从位置 pos 开始向前搜索字符串 s 的前 n 个字符的最后一次出现。
rfind(char c, size_type pos = npos) const:
从位置 pos 开始(默认为字符串的末尾)向前搜索字符 c 的最后一次出现。

ℹ️ℹ️具体操作 

string str = "The rain in Spain falls mainly in the plain.";

// 查找子串的最后一次出现
size_t found = str.rfind("in");
if (found != std::string::npos)
	cout << "Last occurrence of 'in' found at index: " << found << '\n';

// 查找字符的最后一次出现
found = str.rfind('a');
if (found != std::string::npos)
	cout << "Last occurrence of 'a' found at index: " << found << '\n';

// 查找子串的最后一次出现,但不超过索引 10
found = str.rfind("in", 10);
if (found != std::string::npos)
	cout << "Last occurrence of 'in' before index 10 found at index: " << found << '\n';

// 查找不存在的字符串
found = str.rfind("xyz");
if (found == std::string::npos)
	cout << "'xyz' not found.\n";

 rfind 用于找到特定的字符或者子串在字符串中最后一次出现的位置。如果需要从字符串的结尾向前查找,rfind 是个理想的选择。如果想要找字符串中某个子串或字符的第一个出现,应当使用 find 函数。

💧c_str

c_str() 返回一个指向正规C字符串的指针,常量,即以空字符结束的字符数组。这个方法用于获取一个C风格的字符串版本,通常是为了与需要传统C字符串的C语言API兼容。

这是一个非常实用的功能,因为它允许 string 对象以C风格的字符串形式与那些早期设计的或者以纯C编写的接口(APIs)交互。例如,一些系统调用和库函数要求传入一个以 NULL 终止的字符数组(char* 或 const char*),这时你可以使用 c_str() 函数。

 

ℹ️ℹ️具体操作

string str = "Example string";
    
 // 使用c_str()获取C风格的字符串
 const char* cstyle_str = str.c_str();
    
 // 输出C风格的字符串
 cout << cstyle_str << endl;

在上面的例子中,c_str() 返回了一个指向以 NULL 结尾的字符串 'Example string\0' 的指针。这个指针可以被传递给任何需要C风格字符串的函数

需要注意的一点是,由 c_str() 返回的字符数组的生存期与它所属的 string 对象相同,并且如果在 c_str() 调用后对原字符串对象进行了修改(除了添加字符到字符串的末尾以外),返回的字符指针可能就不再有效。因此,最好是在需要使用C风格字符串的时候才调用 c_str(),并且避免在之后修改字符串。

 

💧substr (⭐)

💫string类对象的非成员函数重载   

三、string类的接口一览图 

 接口一览:从文档中整理了一份全部接口的思维导图


文档网站:
cplusplus.com/reference/string/string/?kw=stringicon-default.png?t=N7T8https://cplusplus.com/reference/string/string/?kw=string

字符编码:

【Python】python编程初探2---字符编码,输入和输出,初识数据类型-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/sobercq/article/details/137061735


文档学习小结:

sequences :n. 序列,顺序;继起的事,是sequence的复数形式

features : n.产品特点,特征;容貌;嘴脸(feature 的复数)

specifically : adv. 特意,专门地;明确地,具体地;具体来说,确切地说;局限性地;专门;

instantiation : n. 实例化

handle :  v. 拿;处理,应付;操纵;触(球);经营,管理

default : adj. 默认的

variable :adj. 易变的,多变的;时好时坏的;可变的,可调节的;(数)(数字)变量的;(植,动)变异的,变型的;(齿轮)变速的

assignment :  n. 作业,任务;(工作等的)分配,指派;(财产、权利的)转让,在默认成员函数中,意指赋值。

substring :n. 子串;子链

constructor :n. 构造函数;构造器;建造者

portion :n. (某物的)一部分;(尤指餐馆中食物的)一份,一客;(责任、过失、职责等的)一份,一部分;<法律>(根据法律赠与或遗留给继承人的)一份财产;<古> 命运,天数

spans : v. 跨越;持续;贯穿(span 的第三人称单数)

consecutive : adj. 连续的,不间断的

 assign:v. 分派,布置(工作、任务);分配(某物);指派,派遣;确定(价值、功能、时间、地点);转让(财产、权利)


🛎️感谢各位同伴的支持,本期C++就讲解到这啦,如果你觉得写的不错的话,可以给个一键三连,点赞,关注+收藏,若有不足,欢迎各位在评论区讨论。    

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一阶数字高通滤波器

本文的主要内容包含一阶高通滤波器公式的推导和数字算法的实现以及编程和仿真 1 计算公式推导 1.1.2 算法实现及仿真 利用python实现的代码如下&#xff1a; import numpy as np # from scipy.signal import butter, lfilter, freqz import matplotlib.pyplot as plt #2pifW…

网站工作原理

web发展史 1.0时代不可修改 2.0可修改&#xff0c;比如发微博 有以下问题&#xff1a; 课程2&#xff1a; 静态页面 html 动态页面 php 经过服务端的语言解释器&#xff0c;解析成html文件&#xff0c;剩下的就和静态流程一样 后面三个是web服务器&#xff0c;语言解释器&…

二、使用Django创建一个基础应用

职位管理系统 - 建模 职位名称类别工作地点职位职责职位要求发布人发布日期修改日期 安装django pip install django5.0查看django版本 python -m django --version创建项目 django-admin startproject recruitment启动服务 python manage.py runserver 0.0.0.0:8000创建…

web APIs总结(3)

1. 本地存储介绍&#xff08;重点&#xff09; 数据存储在用户浏览器中设置、读取方便、甚至页面刷新不丢失数据容量较大&#xff0c;sessionStorage和localStorage约 5M 左右 本地存储分类- localStorage 作用: 可以将数据永久存储在本地(用户的电脑), 除非手动删除&#x…

网络编程—— Http的Get请求

http: hyper text transport protocal:超文本传输协议。 http是一种基于客户端-服务器模式的协议(Client-Server)。它规定只能由客户端先发起请求给服务器&#xff0c; 服务器做出响应。 http数据传输以数据报文的形式进行&#xff0c; 客户端向服务器发起的请求叫做请求报文。…

burpsuite抓包响应报文乱码

1、响应报文中的中文信息乱码 2、解决办法 3、设置成功后重新发起请求

无线网络安全技术基础

无线网络安全技术基础 无线网络安全风险和隐患 随着无线网络技术广泛应用,其安全性越来越引起关注.无线网络的安全主要有访问控制和数据加密,访问控制保证机密数据只能由授权用户访问,而数据加密则要求发送的数据只能被授权用户所接受和使用。 无线网络在数据传输时以微波进…

七大医用耗材启动!如何用数屿医械查询医用耗材中标?

近日&#xff0c;国家医保局发布《关于加强区域协同做好2024年医药集中采购提质扩面的通知》&#xff0c;标志着我国医药集中采购进入新阶段&#xff0c;将实现更高效、更规范的采购模式。 医用耗材是指经药品监督管理部门批准使用的&#xff0c;在诊断、治疗、防护和康复等医疗…

AI代理的类型、优势及示例

AI 代理的类型、优势和示例 AI 代理是重塑商业动态的关键技术进步。了解这些代理的运作方式&#xff0c;发现它们的关键优势包括效率、可扩展性和成本效益。我们将探索代理的实例及它们在各领域的应用&#xff0c;为未来的人工智能趋势和对客户体验的影响铺平道路。 想象一支由…

基于粒子群算法优化的长短期记忆神经网络(PSO-LSTM)回归预测

粒子群算法优化的长短期记忆&#xff08;LSTM&#xff09;神经网络用于回归预测是一种结合了进化计算和深度学习的强大方法。 1. 背景介绍 LSTM神经网络 LSTM&#xff08;Long Short-Term Memory&#xff09;是一种特殊的递归神经网络&#xff08;RNN&#xff09;&#xff0c…

AS连接MUMU模拟器

1、下载安装并打开mumu模拟器 mumu模拟器要弄成开发者模式 点击几次这个版本号&#xff0c;会有提示的&#xff0c;变成开发者模式之后&#xff0c;再连接As 2、打开as 打开Terminal窗口 先要cd 进去自己安装的sdk的platform-tools目录下 cd D:\Android\SDK\platform-tools 尝…

拓展类型——枚举

枚举的作用 枚举通常用来约定某个变量的取值范围 使用字面量和联合类型也可以达到约束变量的作用&#xff0c;但是会有不方便的情况 使用字面量和联合类型约束变量的问题 逻辑含义和真实的值会产生混淆&#xff0c;如果修改了真实值&#xff0c;会造成大量代码需要修改 例&…

大猿人话费平台对接程序,仅支持Windows

1、基础参数 输入大猿人系统分配的相关信息&#xff0c;然后保存。 软件运行在左下角显示在平台的余额。 本软件仅会在本地保存您填写的数据。 且您填写的数据仅会在和您的服务器通讯&#xff0c;不会发往其他地方。 请妥善保存好您的数据。 使用本软件造成的一切和本软件…

618哪些好物值得入手?必备数码好物清单分享

618购物节又来了!一大波智能好物来袭!随着科技的日新月异&#xff0c;智能产品已成为我们生活中不可或缺的一部分&#xff0c;它们不仅炫酷&#xff0c;还能让你生活更便捷。想知道今年都有哪些黑科技新品吗?赶紧跟我们一起&#xff0c;我们将详细介绍这些热门好物。一堆超炫酷…

【学习笔记】后端(Ⅰ)—— NodeJS(Ⅰ)

NodeJS 1、概述 1.1、NodeJS是什么 1.2、NodeJS的主要作用 1.3、NodeJS的优点 1.4、NodeJS 与 浏览器 的 JavaScript 对比 1.4.1 ECMAScript 介绍 1.4.2 JavaScript 介绍 1.4.3 TypeScript 介绍2、基础篇 2.1、Buff…

【Python脚本随手笔记】-- 将 “庆余年2” 等信息写入 Txt 文件中

&#x1f48c; 所属专栏&#xff1a;【Python脚本随手笔记】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#…

Gerchberg-Saxton (GS) 和混合输入输出(Hybrid Input-Output, HIO)算法

文章目录 1. 简介2. 算法描述3. 混合输入输出&#xff08;Hybrid Input-Output, HIO&#xff09;算法3.1 HIO算法步骤3.2 HIO算法的优势3.3 算法描述 4. 算法实现与对比5. 总结参考文献 1. 简介 Gerchberg-Saxton (GS) 算法是一种常用于相位恢复和光学成像的迭代算法。该算法最…

深度学习-转置卷积

转置卷积 转置卷积&#xff08;Transposed Convolution&#xff09;&#xff0c;也被称为反卷积&#xff08;Deconvolution&#xff09;&#xff0c;是深度学习中的一种操作&#xff0c;特别是在卷积神经网络&#xff08;CNN&#xff09;中。它可以将一个低维度的特征图&#x…

shell快捷命令与正则表达式

一.高效快捷命令 1.快捷排序——sort 以行为单位对文件内容进行排序&#xff0c;也可以根据不同的数据类型来排序比较原则是从首字符向后&#xff0c;依次按ASCII码值进行比较&#xff0c;最后将他们按升序输出。 语法格式 sort [选项] 参数 cat file | sort 选项 -n 按照数…

LeetCode-102. 二叉树的层序遍历【树 广度优先搜索 二叉树】

LeetCode-102. 二叉树的层序遍历【树 广度优先搜索 二叉树】 题目描述&#xff1a;解题思路一&#xff1a;一个全局队列queue&#xff0c;while queue&#xff1a;去搜集当前所有queue的level解题思路二&#xff1a;背诵版解题思路三&#xff1a; 题目描述&#xff1a; 给你二…