1. string中的字符串操作

首先展示一下string中关于字符串操作有哪些函数。
1.1 c_str和data
在 C++ 中, c_str的作用是返回一个指向以 \0 结尾的C语言风格字符串的常量指针( const char* 类型)。
因为很多C语言的函数(如strlen、strcpy)只支持C风格的char*字符串,而不支持C++的 std::string ,所以c_str常用来实现两种字符串类型的兼容转换。

对于这段代码而言,我先创建了一个string类对象filename,里面存储着字符串"string.cpp",这是我所写代码的文件名,然后调用了fopen函数。

我们来看,fopen这个函数的第一个参数是const char*类型,表示一个文件的地址。如果我直接传filename的话,就会有参数错误,所以还需要调用c_str使我的string对象变成一个常量指针。后面的代码就是打开这个文件并且以只读的形式,然后再把这个文件中的内容打印到调试控制台上。
这里的fopen是C语言的函数,而string类对象filename是C++的内容,这里就很好的体现了C和C++通过c_str函数进行的完美转换。
下一个:data函数

其实data函数和c_str函数的关系就相当于size函数和lenth函数的关系,作用都是一模一样的,只是名字不同和使用频率不同,现状是和C++发展历史有关,在这里不过多赘述。
1.2 get_allocator

在 C++ 中, get_allocator()的作用是返回该容器当前使用的内存分配器对象。
C++ 容器的内存管理由分配器(Allocator) 负责,默认使用std::allocator,它会调用new和delete完成内存的申请与释放;通过get_allocator()可以获取这个分配器实例,用于手动分配和容器同规则的内存,或自定义内存管理策略。它是和内存池相关的。
C++ 里的容器(比如装东西的 "筐子"------ 像 vector、string 这些,就好比家里装鸡蛋的篮子、装米面的袋子)想成是"一个带着管家的筐子"。 这个 "管家" 就是分配器(allocator),它的活儿就一件:给筐子找地方放东西(申请内存)、东西不用了把地方腾出来(释放内存)。而get_allcoator呢,就是 "把这个管家叫出来" 的办法 ------ 跟筐子说 "把你家管家喊出来我见见",筐子就把管家递到跟前儿了。
说的更具体一点,就是:get_allocator就是调用容器的内存分配器对象,让它按照原容器的习惯,再开辟一片内存池用于存储数据。但是一般来说是很少会用到这个函数的,因为大部分只要用原定的内存池就能满足大部分需求了。
1.3 cpoy

下一个函数是:cpoy。大家看这个函数名以及参数类型,应该一眼就能猜出这个函数的作用是:拷贝对象的从pos位置开始的len个字符,放到字符数组s当中去。并且该函数的返回值是复制字符的个数。

因为copy只有拷贝的功能,所以为了更好的展示,我们把s打印出来,这段代码的意思就是,将st1的内容中的从第0个位置(也就是A)开始,拷贝两个字符到字符数组s中,然后用lenth接收copy函数的返回值2。这里为什么还要给s数组添加一个 \0 呢?
因为在 C++ 中,当用 cout 输出字符数组时, cout会从数组的起始位置开始,逐个输出字符,直到遇到 \0 才会停止。如果没有 \0 ,cout会"越界"继续输出数组后面的随机内存内容,直到碰巧遇到一个 \0 为止,这会导致输出结果混乱。所以需要在s数组的第lenth个位置添加一个 \0。然后再打印,就如结果所示。
但是因为copy函数适用于字符数组,为了拷贝还要单独创建一个数组,比较麻烦,所以对于拷贝,我们一般使用下面这个函数。
1.4 substr

substr的核心作用是从一个字符串中截取指定范围的子字符串,返回值是这个新的子字符串(原字符串不会被修改)。这个范围就是从第pos个位置开始,截取len个字符。

这段代码的意思就是,将st1
的从第0个位置开始(就是a),往后截取2个字符,即ab,然后赋值给st2,再将st1和st2打印出来,st1的内容也没有被修改。
1.5 find和rfind

接下来介绍查找函数:find。
对于查找函数来说,如果查找到了就返回第一次遇到该字符或字符串的第一个下标,如果没有查找到就返回npos。
其中参数类型最多的就是size_t pos,它表示find函数开始查找的位置,缺省参数设为0就代表从头开始查找。它可以查找string类对象,字符串,字符。 主要是第三个,它还有一个参数size_t n,它的意思是在原字符串中,从pos位置开始,查找字符s的前n个字符。
接下来展示一个小小的应用:

我们先来看,如果说我要查找字符' . '后面的内容,就可以先调用find函数,用lenth接收返回的下标,再调用substr函数,把从lenth位置开始往后的数据都打印出来。
现在有一个问题:

对于这样一个字符串,我想要找它最后面的后缀,即:" .zip "。该怎么做呢?如果是查找 " . " 这个字符的话 ,那会返回c的前一个位置,如果直接查找zip的话,那那个 " . " 又找不到了。这个时候,就需要利用到另一个函数:refind。

rfind的find的作用就是,一个是从后往前找,一个是从前往后找。既然我想要找的是最后一个后缀,那就使用rfind就好了。

在这里我创建了一个函数,先传递一个string类对象过来,然后调用rfind函数从后往前找 " . " ,如果找到了,那下标 i 就不等于npos,所以就调用substr函数,从第 i 个位置开始向后把剩下的内容都打印出来。如果没有找到,那下标 i 就等于npos,那就返回一个匿名函数,就跟return 0差不多。
为了更深入的了解find和rfind,接下来需要做一道题目:如果给你任意一个网址,其中包括协议,域名和要访问的资源,该怎么把这个网址的这三个部分,分别查找出来。
就以这个网址为例:

它是我们在学习C++的时候会用到的查文档的cplusplus的官方网址。
大家会发现,协议的前面有一个冒号,那只要找到冒号不就有了协议嘛,所以第一步是要先找冒号。那么就可以直接调用find函数:

找到" :"之后,就截取下标从0开始,打印i1个字符,就可以取到协议。
接下来需要取域名,因为对于所有协议后面三个字符的格式都是固定的,在协议后面一定是" :/ / "这三个字符,并且在域名中间是不会有" / "这个符号的。那域名的末尾的后面一个字符又一定是" / "这个符号,那就给我们确定位置提供了很大的帮助:

我只需要取出i2和i3的位置,然后截取i2和i3之间的字符就可以了。

因为i1的位置是" :"这个字符,往后加三,那i2就是" l "这个字符,然后从i2的位置找" / ",并用i3接收返回值,然后再截取从i2到i3-i2的长度的字符,我们先中途把结果打印一下,观察为正确结果。
接下来要取出的是资源部分,因为从i3代表的那个" / "后面都是资源部分,所以直接把后面的内容全部取出来就可以:

这样一份大致的代码就写完了。
接下来需要看这四个函数:

因为重合度太高,为了避免文章冗余,在这里我只讲第一个find_first_of中的一个函数重载,剩下的部分大家可以根据我所讲的一一分析,内容都是大致相同的。

find_first_of的作用是查找原字符串中有没有str这个字符串中的任意一个字符,如果有的话,就返回原字符串中的下标。

对于string类的对象str来说,调用find_first_of 函数,查找str中有没有"aeiou"这个字符串中的任意一个字母,如果查找到,那found就不等于npos,那就将str字符串中的下标为found的字符给改成 ' * ',然后再调用find_first_of函数,从found+1的位置开始,继续查找有没有"aeiou"这个字符串中的任意一个字符。运行结果就是这样:

其实对于这个find_first_of函数,倒不如改成叫find_any_of函数,因为是找字符串中的任意一个字符,所以用any反而更恰当。
同样的,find_last_of就是从后往前找,find_last_not_of和find_first_not_of就是找和字符串中不相同的字符。在这里就不过多赘述,大家可以自行探索。
1.6 compare

compare的核心作用是按字符的 ASCII 码值逐字符比较两个字符串(或字
符串的子段),返回一个整数表示比较结果,是比普通的 == 、< 、> 更灵活、更精准的字符串比较工具。
简单说,compare不只是判断 "相等 / 不相等",还能明确告诉我们 "谁大谁小",看compare的参数类型,还可以比较两个字符串的某个部分。其实类似于C语言中的strcmp。

以这段代码为例,展示的是compare的第一个函数重载,比较了st1和st2的字符串中的字符对应的ASCII码值,因为是通过st1调用,所以这里的意思就是:看看st1的字符的ASCII是否都大于st2,如果是,就返回一个正值,如果不是,那就返回一个负值或零。在这里,因为st1的第一个字符a对应的ASCII码值是97,而b的ASCII码值是98,因为97<98,所以返回了一个负值。
剩下的几个函数重载在这里就不过多赘述。其实compare的使用频率非常少,这是因为C++在这里还重载了一个关系运算符:
2. string中的非成员函数

刚刚说的关系运算符就是:
2.1 relational operators

这里的关系更加的丰富完善且方便。这里包含了==、!=、<、<=、>、>=,以及对应的不同类型,即:string和string,char和string,string和char。

这里给大家举个例子,这也是比较ASCII码值,因为两个字符串比到st1的d和st2的e时,因为e的ASCII码值大于d的ASCII码值,所以就返回零或者一个负数,表示st1不等于st2。然后我们用一个变量 i 接收它们的返回值。
并且大家需要注意的是:

对于关系运算符重载来说,它并不是成员函数。这主要是因为参数类型的第二个重载:(const char* lhs, const string& rhs); 因为如果是成员函数,那么在调用的时候,第一个参数就绝对是const string& ,因为是一个string类对象在调用成员函数。所以这里只能定义成全局函数,不能作为成员函数。
2.2 operator+

operator+和operator+=为什么一个是全局函数,一个是成员函数呢?其实这也是因为operator+要支持( const char* , cosnt string& )的这个参数类型。" + "这个操作符大家应该不陌生,和+=不一样的是,+不会改变本身的值。
2.3 getline

getline的作用是是用于读取一行文本的输入函数,核心作用是从指定输入流 cin 中读取字符,直到遇到换行符为止,并将读取的内容(不包含换行符本身)存储到字符串对象中。它解决了cin>>读取字符串时 "遇到空格 / 制表符就停止" 的问题,能完整读取整行输入。
为了能让大家更好的理解getline的作用,用一道题目为大家讲解:
这道题的题目是:求字符串中的最后一个单词的长度。链接如下:字符串最后一个单词的长度_牛客题霸_牛客网 我们看实例2,会发现单词和单词之间是用空格 " " 隔开的,并且是求最后一个单词,那也就意味着,我只需要找到最后一个空格,然后从空格的下一个位置开始,打印到最后就可以了。

如果我们这样写,用于输入所给出的案例,就会遇到一个问题,因为对于cin>>来说,如果是一个连续的单词串,就像这样:"C++ is good."它会在读到第一个空格的时候就停下来,后面的内容就不存储了,这就导致我们的st1的内容就是:C++。这与我们的想法相违背,这时getline的作用就体现出来了,直接调用getline函数,可以读取到换行符前的所有内容:

接下来把这个题目的代码补充完整:

另外getline还有一个函数重载:(istream& is, string& str, char delim),最后一个参数delim表示的是,指定在原字符串当中,如果遇到了delim就停止:

就像这样,在这里我传了一个符号' ! ',我随机输入了一个中间包含' !'的字符串,当我把st1打印出来的时候,只打印了' !'前面的部分,说明st1只存储了这些内容。
到这里,关于string的内容大致就是这些,希望大家能自己动手操作熟练一下。因为是第一次接触这样的类型,需要查阅文档的等,所以讲的内容会比较多,到后面的vector和list等类就会讲的比较快。