【C++修炼之路】8. string类详解

news2024/11/30 6:51:10

在这里插入图片描述
每一个不曾起舞的日子都是对生命的辜负

C++之string类

  • 本节目标
  • 1. string类概览
    • 1.1 string的由来
    • 1.2 string函数列表
  • 2.string常用接口
    • 1. 初始化
    • 2. string::npos
    • 3. c_str()
    • 4. 获取长度(length、size)
    • 5. 容量(size、capacity)
    • 6. 插入(insert)
    • 7. 替换(replace)
    • 8. 添加(append、push_back、+=)
    • 9. 赋值(assign)
    • 10. 删除与判空(erase、clear、empty)
    • 11. 剪切(substr)
    • 12. 比较(compare)
    • 13. 交换(swap)
    • 14. 反转(reverse)
    • 15. 迭代器(iterator)
      • 15.1 正向迭代器
      • 15.2 反向迭代器
      • 15.3 const迭代器
    • 16. 搜索与查找(find等函数)
      • 16.1 find()函数
      • 16.2 rfind函数
      • 16.3 find_xxx_of()函数(功能强大,但不常用)
  • 3. string类的应用
    • 3.1 三种遍历方式
    • 3.2 替换空格
    • 3.3 通过find取后缀
    • 3.4 getline的应用
  • 4. string总结

本节目标

熟练掌握各种string类的函数并将其应用。

注:本文参考以下两篇优秀文章,将其结合并加上额外的知识用自己的理解进行描述:

C++之string类型详解

C++string类型详解

1. string类概览

1.1 string的由来

之所以抛弃char*的字符串而选用C++标准程序库中的string类,是因为他和前者比较起来,不必担心内存是否足够、字符串长度等等,而且作为一个泛型类出现,他集成的操作函数足以完成我们大多数情况下(甚至是100%)的需要。我们可以用 = 进行赋值操作,== 进行比较,+ 做串联(是不是很简单?)。我们尽可以把它看成是C++的基本数据类型。

C++中对于string的定义为:typedef basic_string<char> string; 也就是说C++中的string类是一个泛型类,由模板而实例化的一个标准类,本质上不是一个标准数据类型。

在我们的程序中使用string类型,我们必须包含头文件 。如下:
#include<string> using namespace std; 此语句必不可少,否则有的编译器无法识别

1.2 string函数列表

begin得到指向字符串开头的Iterator
end得到指向字符串结尾的Iterator
rbegin得到指向反向字符串开头的Iterator
rend得到指向反向字符串结尾的Iterator
size得到字符串的大小
length和size函数功能相同
max_size字符串可能的最大大小
capacity在不重新分配内存的情况下,字符串可能的大小
empty判断是否为空
operator[]取第几个元素,相当于数组
c_str取得C风格的const char* 字符串
data取得字符串内容地址
operator=赋值操作符
reserve预留空间
swap交换函数
insert插入字符
append追加字符
push_back追加字符
operator+=+= 操作符
erase删除字符串
clear清空字符容器中所有内容
resize重新分配空间
assign和赋值操作符一样
replace替代
copy字符串到空间
find查找
rfind反向查找
find_first_of查找包含子串中的任何字符,返回第一个位置
find_first_not_of查找不包含子串中的任何字符,返回第一个位置
find_last_of查找包含子串中的任何字符,返回最后一个位置
find_last_not_of查找不包含子串中的任何字符,返回最后一个位置
substr得到字串
compare比较字符串
operator+字符串链接
operator==判断是否相等
operator!=判断是否不等于
operator<<//td>判断是否小于
operator>>从输入流中读入字符串
operator<<字符串写入输出流
getline从输入流中读入一行

对于这些函数,我们在下面将会逐个讲解:

2.string常用接口

对于这些用法来说,下面传入的参数个数在同一个用法中都有所差异,这是因为每一个成员函数都支持了重载。

在介绍下述接口之前,需要知道的是,由于每一个函数都有重载,因此有的有const的重载,有的没有,其实这是以函数的需求从而判断其有无const类型的重载函数,这里提前进行总结:

  1. 只读功能函数 const版本
  2. 只写功能函数 非const版本
  3. 读写功能的函数 const+非const版本

1. 初始化

初始化有两种方式,其中使用等号的是拷贝初始化,不使用等号的是直接初始化。(注释后面是打印的结果)

但对于使用等号的和str(str1),即一个变量通过另一变量初始化的,都是拷贝构造。(深拷贝)

string str1 = "hello world";      // str1 = "hello world"
string str2("hello world");       // str2 = "hello world"
string str3 = str1;               // str3 = "hello world"
string str4(str2);                // str4 = "hello world"
string str5(10,'h');              // str5 = "hhhhhhhhhh"
string str6 = string(10,'h');     // str6 = "hhhhhhhhhh"
string str7(str1,6);              // str7 = "world"     从字符串str1第6个字符开始到结束,拷贝到str7中
string str_7(str1,6,3); // str_7 = "wor"     从字符串str1第6个字符开始的三个字符,拷贝到str7中
string str8 = string(str1,6);     // str8 = "world"
string str9(str1,0,5);            // str9 = "hello"     从字符串str1第0个字符开始,拷贝5个字符到str9中
string str10 = string(str1,0,5);  // str10 = "hello"
char c[] = "hello world";
string str11(c,5);                // str11 = "hello"    将字符数组c的前5个字符拷贝到str11中
string str12 = string(c,5);       // str12 = "hello"

2. string::npos

我们观察一下上面str7与str_7的区别,想必大家已经看出,这里的str7和str_7是同一个重载函数,并且这个函数具有缺省值,当我们不传入最后一个参数时,其就会一直拷贝到字符串的末尾为止。那这个缺省参数是什么呢?我们查阅文档得知,是npos:image-20221103131936946image-20221103132229293我们发现,npos的值规定为-1,但实际上因为是size_t类型,所以这是一个无符号的数字,即此-1并不是十进制的-1,而是:4294967295image-20221103132447610

因此,此位置重载在不输入数值时默认为此值,也就能够遍历到字符串的末尾了。

此外,对于内置的string类,是支持运算符重载的,因此同样的也支持流的相关重载,即cin、cout:

string str1 = "hello world";     
cout << "str1 = " << str1;    //输出: str1 = hello world

所以我们可以把string这个内部类当成内置类型使用。

3. c_str()

image-20221103154600994

对于string类来说,其内部有这么一个成员变量,c_str,正如此图,c_str本身和指向的值均不能改变,返回值是char*实际上返回的就是string类中的内容的地址,也就是字符串的地址。

image-20221103154953223

那c_str有什么作用呢?事实上对于一些线程,网络,Linux内核等都是通过C实现的,因此c_str很好的充当了一个C++中string与C之间的互通,因为我们知道,对于string定义的变量名,不是内部字符串的地址,因此就出现了c_str()返回内容的地址,从而解决这个问题。

演示:

image-20221103155916051

结果:

image-20221103155949557

这样就将其内容正确的打开了。

4. 获取长度(length、size)

length()函数与size()函数均可获取字符串长度。但除了string,其他类型就只有size()。

string str = "hello world";
cout << str.length() << str.size();     // 11   11

当str.length()与其他类型比较时,建议先强制转换为该类型,否则会意想之外的错误。
比如:-1 > str.length() 返回 true。

5. 容量(size、capacity)

对于size和capacity来说,大家在学了顺序表之后并不陌生,size是实际长度,而capacity代表着容量的大小,对于string类来说,其也具有这样的成员变量(对应值C语言顺序表中结构体内部的的size、capacity),而这里的扩容规则在每一个平台也是不一样的,比如我的linux和vs2019二者之间就有很大的区别,不过我们并不需要关心他,因为string作为内部类,其扩容的机制已经被写在该类之中。

string str;     
str += "hello world hello world";

image-20221103124701051

image-20221103124804439

可以看出,其大概是是扩容了二倍的大小。

此外,还有其他函数也属于容量的范畴:

image-20221103143927322

resize可以改变成员的size()的大小。

reserve可以改变成员的capacity()的大小。

image-20221103144426584

而对于max_size(),这里我们只打印结果就知道其具体的含义:

image-20221103160219800

6. 插入(insert)

image-20221103150409036

string str = "hello world";
string str2 = "hard ";
string str3 = "it is so happy wow";

//s.insert(pos,n,ch)        在字符串s的pos位置上面插入n个字符ch
str.insert(6,4,'z');        // str = "hello zzzzworld"

//s.insert(pos,str)         在字符串s的pos位置插入字符串str
str.insert(6,str2);         // str = "hello hard world"

//s.insert(pos,str,a,n)     在字符串s的pos位置插入字符串str中位置a到后面的n个字符
str.insert(6,str3,6,9);     // str = "hello so happy world"

//s.insert(pos,cstr,n)      在字符串s的pos位置插入字符数组cstr从开始到后面的n个字符
//此处不可将"it is so happy wow"替换为str3
str.insert(6,"it is so happy wow",6);       // str = "hello it is world"

虽然有这样的接口,但是我们知道对于类似于顺序表的结构来说,这样的插入,实际上底层都会将后面的数据进行挪动,因此效率难免会低一些。

7. 替换(replace)

替换与插入对应,对比理解更为简单。

image-20221103150138419

string str = "hello world";
string str2 = "hard ";
string str3 = "it is so happy wow";

//s.replace(p0,n0,n,ch)           删除p0开始的n0个字符,然后在p0处插入n个字符ch
str.replace(0,6,4,'z');           // str = "zzzzworld"

//s.replace(p0,n0,str)            删除从p0开始的n0个字符,然后在p0处插入字符串str
str.replace(0,6,str2);            // str = "hard world"

//s.replace(p0,n0,str,pos,n)      删除p0开始的n0个字符,然后在p0处插入字符串str中从pos开始的n个字符
str.replace(0,6,str3,6,9);        // str = "so happy world"

//s.replace(p0,n0,cstr,n)         删除p0开始的n0个字符,然后在p0处插入字符数组cstr的前n个字符
//此处不可将"it is so happy wow"替换为str3
str.replace(0,6,"it is so happy wow",6);        // str = "it is world"

8. 添加(append、push_back、+=)

append函数用在字符串的末尾添加字符和字符串。(同样与插入、替换对应理解)而push_back只适用于添加单个字符,此外,对于添加来说,如果是在末尾添加字符或者字符串我们仍然可以像初始化中的拷贝构造一样,即通过+=进行添加。

image-20221103150246351

image-20221103150309397

image-20221103150328171

string str = "hello world";
string str2 = "hard ";
string str3 = "it is so happy wow";
string str4 = "hello world"

//s.append(n,ch)           在当前字符串结尾添加n个字符c
str.append(4,'z');         // str = "hello worldzzzz"

//s.append(str)            把字符串str连接到当前字符串的结尾
str.append(str2);          // str = "hello worldhard "

//s.append(str,pos,n)      把字符串str中从pos开始的n个字符连接到当前字符串的结尾
str.append(str3,6,9);      // str = "hello worldso happy "

//append(cstr,int n)       把字符数组cstr的前n个字符连接到当前字符串结尾
//此处不可将"it is so happy wow"替换为str3
str.append("it is so happy wow",6);      // str = "hello worldit is "

str4.push_back('c'); // str4 = "hello worldc"
str4 += 'c';         // str4 = "hello worldcc"
st4 += "cdef";       // str4 = "hello worldcccdef"

9. 赋值(assign)

image-20221103150026160

赋值也是一种初始化方法,与插入、替换、添加对应理解较为简单。

string str;
string temp = "welcome to my blog";

//s.assign(n,ch)             将n个ch字符赋值给字符串s
str.assign(10,'h');          // str = "hhhhhhhhhh"

//s.assign(str)              将字符串str赋值给字符串s
str.assign(temp);            // str = "welcome to my blog"

//s.assign(str,pos,n)        将字符串str从pos开始的n个字符赋值给字符串s
str.assign(temp,3,7);        // str = "come to"

//s.assaign(cstr,n)          将字符数组cstr的前n个字符赋值给字符串s
//此处不可将"it is so happy wow"替换为temp
str.assign("welcome to my blog",7);     // str = "welcome"

10. 删除与判空(erase、clear、empty)

对于clear,其具有清空的功能,也就是从任意的字符串使用clear,都会将其清空至空字符串;对于erase来说,其可以指定的删除,也就是说可以删除一部分,也可以删除全部。

string str = "welcome to my blog";

//s.erase(pos,n)           把字符串s从pos开始的n个字符删除
str.erase(11,3);           // str = "welcome to blog"
str.clear();               // str = ""
str.empty();               // 返回1

clear()实际上不会将capacity的空间也清除掉,即size会改变,但capacity并不会改变。

对于empty来说,实际上是判断是否为空的函数,是空就返回1,不是空就返回0,因此empty是根据size()是否为0判断的。

11. 剪切(substr)

image-20221103150054578

string str = "The apple thinks apple is delicious";

//s.substr(pos,n)                      得到字符串s位置为pos后面的n个字符组成的串
string s1 = str.substr(4, 5);           // s1 = "apple"

//s.substr(pos)                        得到字符串s从pos到结尾的串
string s2 = str.substr(17);            // s2 = "apple is delicious"

string s3 = str.substr(4, 100);        // s3 = "apple thinks apple is delicious"
string s4 = str.substr(4, string::npos);// s4 = "apple thinks apple is delicious"

12. 比较(compare)

两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇’\0’为止。若是遇到‘\0’结束比较,则长的子串大于短的子串,如:“9856” > “985”。如果两个字符串相等,那么返回0,调用对象大于参数返回1,小于返回-1。

image-20221103150438655

string str1 = "small leaf";
	string str2 = "big leaf";

	//s.compare(str)                     比较当前字符串s和str的大小
	cout << str1.compare(str2);                   // 1

	//s.compare(pos,n,str)               比较当前字符串s从pos开始的n个字符与str的大小
	cout << str1.compare(2, 7, str2);               // -1

	//s.compare(pos,n0,str,pos2,n)       比较当前字符串s从pos开始的n0个字符与str中pos2开始的n个字符组成的字符串的大小
	cout << str1.compare(6, 4, str2, 4, 4);           // 0

	//s.compare(pos,n0,cstr,n)           比较当前字符串s从pos开始的n0个字符与字符数组cstr中前n个字符的大小
	//此处不可将"big leaf"替换为str2
	cout << str1.compare(6, 4, "big leaf", 4);       // 1

13. 交换(swap)

string str1 = "small leaf";
string str2 = "big leaf";

//或者str1.swap(str2)  ,输出结果相同
swap(str1,str2);        // str1 = "big leaf"     str2 = "small leaf"
swap(str1[0],str1[1]);  // str1 = "ibg leaf"

14. 反转(reverse)

反转字符串。

string str = "abcdefghijklmn";
	string::iterator it1 = str.begin();
	string::iterator it2 = str.end();
	reverse(str.begin(), str.end());       // str = "nmlkjihgfedcba"
	reverse(it1, it2);                      // 又反转了一次:str = "abcdefghijklmn"

对于str.begin()和str.end()类型,其返回值可以看成指针,但实际上并不是指针,因此我们用char* 定义的it1和it2是不对的,其在不同的平台上有不同的含义,事实上其有着迭代器的作用。(下面讲解迭代器的使用)

15. 迭代器(iterator)

迭代器实际上是一个像指针一样的东西,这是对行为来说的。需要注意的是,这里不能用char*,虽然对于vs这个平台可以使用char*,但难保其他的平台是char*,因此迭代器的底层不一定是char*,这在模拟实现中将会详细介绍。(对于迭代器来说,只要会用string类,那么vector、list等容器与其方法是一样的)

15.1 正向迭代器

//迭代器  -- 通用的访问形式
string s1("1234");
string::iterator it1 = s1.begin();//s.begin() 返回字符串s第一个字符的位置
while (it1 != s1.end())//s.end()  返回字符串s最后一个字符串的后一个位置
{
    *it1 += 1;
    ++it1;
}
it1 = s1.begin();
while (it1 != s1.end())
{
    cout << *it1 << " ";
    ++it1;
}           // 输出:2 3 4 5 

15.2 反向迭代器

当然,还有反向迭代器,其访问顺序与默认的迭代器相反。

string s1("1234");
string::reverse_iterator rit = s1.rbegin();
while (rit != s1.rend())
{
    cout << *rit << " ";
    ++rit;
} // 输出: 4 3 2 1

此外,我们发现此定义名过长,因此我们也可以用auto去定义变量rit接收rbegin()。

15.3 const迭代器

当我们需要只读的时候,为了避免改变其中的值,在迭代器使用时我们就会选择const迭代器,顾名思义const迭代器能够保护迭代指向的变量不被改变,那我们实际来看一下const迭代器如何使用:

void Print(const string& s)
{
	string::const_iterator it = s.begin();//正向
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	string::const_reverse_iterator rit = s.rbegin(); //反向 可以用auto代替,即:auto rit = s.rbegin();
	while (rit != s.rend())
	{
		cout << *rit << " ";
		rit++;
	}
	cout << endl;
}
int main()
{
	string s1("1234");
	Print(s1);
	return 0;
}

image-20221103141131313

总结:迭代器经过上述的描述,一共有四种,即正向、反向、正向const、反向const,其不能混合定义,否则会出现错误。

16. 搜索与查找(find等函数)

16.1 find()函数

image-20221103145925583

string str = "The apple thinks apple is delicious";     //长度34
string key = "apple";

//s.find(str)            查找字符串str在当前字符串s中第一次出现的位置
int pos1 = str.find(key);                  // 4

//s.find(str,pos)        查找字符串str在当前字符串s的[pos,end]中第一次出现的位置
int pos2 = str.find(key, 10);              // 17

//s.find(cstr,pos,n)     查找字符数组cstr前n的字符在当前字符串s的[pos,end]中第一次出现的位置
//此处不可将"delete"替换为str2(如果定义str2 = "delete")
int pos3 = str.find("delete", 0, 2);       // 26

//s.find(ch,pos)         查找字符ch在当前字符串s的[pos,end]中第一次出现的位置
int pos4 = str.find('s', 0);               // 15

16.2 rfind函数

image-20221103160547001

rfind与find的区别就是:find是从左往右找(即从前往后),而rfind是从后往前找。

string str = "The apple thinks apple is delicious";     //长度34
string key = "apple";

//s.rfind(str)            查找字符串str在当前字符串s中最后一次出现的位置
int pos5 = str.rfind(key);                 // 17

//s.rfind(str,pos)        查找字符串str在当前字符串s的[0,pos+str.length()-1]中最后一次出现的位置
int pos6 = str.rfind(key, 16);             // 4

//s.rfind(cstr,pos,n)     查找字符数组cstr前n的字符在当前字符串s的[0,pos+n-1]中最后一次出现的位置
//此处不可将"apple"替换为key
int pos7 = str.rfind("apple", 40, 2);      // 17

//s.rfind(ch.pos)         查找字符ch在当前字符串s的[0,pos]中最后一次出现的位置
int pos8 = str.rfind('s', 30);             // 24

16.3 find_xxx_of()函数(功能强大,但不常用)

xxx是需要确定的名字,因此下面将介绍这种类型的查找函数:(4个)

  1. find_first_of

image-20221103162252514

// s.find_first_of(str)                查找字符串str中的任意字符在当前字符串s中第一次出现的位置
int pos1 = str.find_first_of(key);                // 2

//s.find_first_of(str,pos)             查找字符串str中的任意字符在当前字符串s的[pos,end]中第一次出现的位置
int pos2 = str.find_first_of(key, 10);            // 11

//s.find_first_of(cstr,pos,n)          查找字符串str前n个任意字符在当前字符串s的[pos,end]中第一次出现的位置
//此处不可将"aeiou"替换为key
int pos3 = str.find_first_of("aeiou", 7, 2);      // 17

//s.find_first_of(ch,pos)              查找字符ch在当前字符串s的[pos,end]中第一次出现的位置
int pos4 = str.find_first_of('r', 0);             // 6

最好的解释就是举例子:image-20221103162530959

即找到每个str中的字符进行替换,与find和rfind的区别是:此查找找的是字符串中的所有字符,而不是字符串。下面的几个同样如此:

  1. find_last_of

image-20221103162723155

//s.find_last_of(str)                 查找字符串str中的任意字符在当前字符串s中最后一次出现的位置
int pos1 = str.find_last_of(key);                      // 27

//s.find_last_of(str,pos)             查找字符串str中的任意字符在当前字符串s的[0,pos]中最后一次出现的位置
int pos2 = str.find_last_of(key, 15);                  // 11

//s.find_last_of(cstr,pos,n)          查找字符串str前n个任意字符在当前字符串s的[0,pos]中最后一次出现的位置
//此处不可将"aeiou"替换为key
int pos3 = str.find_last_of("aeiou", 20, 2);           // 17

//s.find_last_of(str)                 查找字符ch在当前字符串s的[0,pos]中最后一次出现的位置
int pos4 = str.find_last_of('r', 30);                  // 28

  1. find_first_not_of

image-20221103162858501

//s.find_first_not_of(str)             查找字符串str之外的任意字符在当前字符串s中第一次出现的位置
int pos1 = str.find_first_not_of(key);                 // 0

//s.find_first_not_of(str,pos)         查找字符串str之外的任意字符在当前字符串s的[pos,end]中第一次出现的位置
int pos2 = str.find_first_not_of(key, 10);             // 10

//s.find_first_not_of(cstr,pos,n)      查找字符串str前n个之外任意字符在当前字符串s的[pos,end]中第一次出现的位置
//此处不可将"aeiou"替换为key
int pos3 = str.find_first_not_of("aeiou", 7, 2);       // 7

//s.find_first_not_of(str)             查找字符ch之外任意字符在当前字符串s的[pos,end]中第一次出现的位置
int pos4 = str.find_first_not_of('r', 0);              // 0

  1. find_last_not_of

image-20221103162953286

//s.find_last_not_of(str)             查找字符串str之外的任意字符在当前字符串s中最后一次出现的位置
int pos1 = str.find_last_not_of(key);                  // 29

//s.find_last_not_of(str,pos)         查找字符串str之外的任意字符在当前字符串s的[0,pos]中最后一次出现的位置
int pos2 = str.find_last_not_of(key, 15);              // 15

//s.find_last_not_of(cstr,pos,n)      查找字符串str前n个之外任意字符在当前字符串s的[0,pos]中最后一次出现的位置
//此处不可将"aeiou"替换为key
int pos3 = str.find_last_not_of("aeiou", 20, 2);       // 20

//s.find_last_not_of(str)             查找字符ch之外任意字符在当前字符串s的[0,pos]中最后一次出现的位置
int pos4 = str.find_last_not_of('r', 30);              // 29

3. string类的应用

3.1 三种遍历方式

int main()
{
    string s1("1234");
	// 遍历他
	// 1、下标 []
	for (size_t i = 0; i < s1.size(); ++i)
	{
		s1[i]++;//实际上这是operator[]()的运算符重载
	}
	//s1[10];
	cout << s1 << endl;
	// 2、范围for
	for (auto& ch : s1)
	{
		ch--;
	}
	cout << s1 << endl;
	// 反转一下
	size_t begin = 0, end = s1.size() - 1;
	while (begin < end)
	{
		swap(s1[begin++], s1[end--]);
	}
	cout << s1 << endl;
	//reverse(s1.begin(), s1.end()); 算法头文件中的函数,直接使用即可。
	cout << s1 << endl;
	// 3、迭代器  -- 通用的访问形式
	string::iterator it1 = s1.begin();
	while (it1 != s1.end())
	{
		*it1 += 1;
		++it1;
	}
	it1 = s1.begin();
	while (it1 != s1.end())
	{
		cout << *it1 << " ";
		++it1;
	}
	cout << endl;
}

3.2 替换空格

题目:替换空格

解法1:

对于这道题,如果按照c语言的方式会很麻烦,但是通过C++string中的函数,我们可以先find,再replace:

class Solution {
public:
    string replaceSpace(string s) {
        size_t pos = s.find(' ');
        while(pos != string::npos)
        {
            s.replace(pos, 1, "%20");
            pos = s.find(' ',pos+3);//+3是优化
        }
        return s;
    }
};

+3实际上就是对代码的优化,因为%20就对应了3个位置,因此+3跳过这三个字符查找的效率更快。

image-20221103151055144

解法2: 我们还可以新建一个string的空字符串,遍历传入的string s如果没有碰到空格就+=该字符,碰到了空格就+=%20

class Solution {
public:
    string replaceSpace(string s) {
        string ret;
        ret.reserve(s.size()*3);// 优化,提前开好空间,省去后续扩容的消耗
        for(auto ch : s)
        {
            if(ch != ' ')
            {
                ret += ch;
            }
            else
            {
                ret += "%20";
            }
        }
        return ret;
    }
};

image-20221103154142377

ret.reserve(s.size()*3);是面对全是空格的情况,这样的极端情况就可以作为扩容的标准。这样我们发现,以空间换时间的做法就可以有效的提高效率。

3.3 通过find取后缀

如果我们想确定一个文件是什么类型,我们需要知道其后缀,最后一个.后面的名字就是其后缀,那我们就可以先通过rfind()找到最后一个.的位置,再通过substr拷贝下来,这样就获得了相应文件的后缀了。(找最后一个.是由于在Linux下的文件可能存在类似于test.cpp.zip.tar的文件名,而其文件类型其实是tar)

void test_string11()
{
	string file;
	cin >> file;
	size_t pos = file.rfind('.');
	if (pos != string::npos)
	{
		string suffix = file.substr(pos);
		cout << suffix << endl;
	}
}

image-20221103161756312

3.4 getline的应用

将getline放在这里是因为这里直接利用会更具体的描述。题目:

HJ1 字符串最后一个单词的长度

这道题如果我们直接用scanf或者cin的话,对于输入hello nowcoder来说,实际上输入的只有hello,因为到空格就截止了,因此这里就需要了getline输入,getline的作用是到换行符时才结束输入。那我们看看代码实现:

#include <iostream>
#include<string>
using namespace std;

int main()
{
    string s;
    getline(cin , s);
    size_t pos = s.rfind(' ');
    cout << s.size() - pos - 1 <<endl;
    return 0;
}

image-20221103164813120

4. string总结

本文详细介绍了string中各个函数功能以及接口,通过这些的灵活运用,才能更好的掌握C++,而对于这些函数的底层实现,下一篇将用的模拟实现string加深对各种函数的理解。

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

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

相关文章

牛客网-《刷C语言百题》第三期

✅作者简介&#xff1a;嵌入式入坑者&#xff0c;与大家一起加油&#xff0c;希望文章能够帮助各位&#xff01;&#xff01;&#xff01;&#xff01; &#x1f4c3;个人主页&#xff1a;rivencode的个人主页 &#x1f525;系列专栏&#xff1a;《C语言入门必刷百题》 &#x…

【HTML5期末大作业】制作一个简单HTML我的班级网页(HTML+CSS+JS)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

微信小程序 |从零实现酷炫纸质翻页效果

&#x1f4cc;个人主页&#xff1a;个人主页 ​&#x1f9c0; 推荐专栏&#xff1a;小程序开发成神之路 --【这是一个为想要入门和进阶小程序开发专门开启的精品专栏&#xff01;从个人到商业的全套开发教程&#xff0c;实打实的干货分享&#xff0c;确定不来看看&#xff1f; …

保姆级透明背景动画导出 —— json格式(Mac系统)

日常能想到的小动画基本就是使用ps导出GIF动画/AE通过插件直接导出GIF&#xff0c;方便快捷&#xff1b;但最近工作中遇到了关于透明背景导出GIF动画的问题&#xff1a;导出的GIF动画有白边&#xff0c;有锯齿感。 网上找了一大堆教程&#xff0c;主要原因出在GIF格式本身的问…

黑马程序员Java实战项目--- ATM系统

项目介绍与功能演示 黑马银行ATM系统技术选型分析&#xff1a; 学习本项目&#xff0c;你将至少得到如下收获&#xff1a; 1、优秀的面向对象编程能力。 2、 清晰、缜密的业务、数据分析能力。 3、熟练使用程序流程技术来控制计算机完成自己的想法。 4、形成良好的编码习惯…

【面试题】移除数组删除重复项合并数组

1️⃣ 原地移除数组中所有的元素val 【OJ链接】 2️⃣ 删除排序数组中的重复项 【OJ链接】 3️⃣ 合并两个有序数组 【OJ链接】 1️⃣ 原地移除数组中所有的元素val 题目要求是要在数组中&#xff0c;把指定的数字全部去除&#xff0c;题目中也是强调了空间复杂度要求为O(1)&am…

项目经理要“谋定而后动,知止而有得,万事皆有法,不可乱也”

出自《大学》&#xff0c;“谋定而后动&#xff0c;知止而有得”的含义为谋划准确周到而后行动&#xff0c;知道目的地&#xff08;合适的时机收手&#xff09;才能够有所收获。 谋定而后动是告诉我们做任何事一定要进行谋划部署&#xff0c;做好准备&#xff0c;我们才能开始行…

数据结构作业:时间复杂度和二叉树

计算时间复杂度&#xff1a; int x0,i,j; 1 for(i1;i<n;i) n { for(j1;j<2*i;j) 2(n-1) { x; 2(n-1)*n } } 2(n-1)*n2n^2-2n 修改后的运行次数函数中&#xff0c;只保留最高阶项。 所以时间复杂度为 &#xff1a;…

老生常谈的商城系统(Asp.Net+uniapp)

真正的大师,永远都怀着一颗学徒的心&#xff01; 最近几天合肥是真热 这时候就应该宅在家里 吃着西瓜 啃着鸭脖 喝着啤酒 刷着剧 想想也太美好了吧 哈哈 我得醒醒 写完这篇推荐 吃吃喝喝去了 一、项目简介 今天推荐一款商城系统&#xff0c;虽然比较老生常谈了&#xff0…

【项目】若依框架如何实现批量导入,并解析出表中内容返回给前端? - poi依赖

文章目录实现效果&#xff1a;具体实现步骤&#xff1a;扩展实例内容&#xff1a;poi工具包ExcelHandlerAdapter接口ExcelUtil.java代码controllerserviceserviceImplmapper.javamapper.xmlTnProductProperty.java实现效果&#xff1a; 点击“导入excl”按钮 --> 弹出“文件…

MSDC 4.3 接口规范(27)

MSDC 4.3 接口规范&#xff08;27&#xff09;7.4.4 代码7.4.5 更新组呼业务7.4.5.1 接口函数7.4.5.2 先决条件7.4.5.3 说明7.4.5.4 调用流程7.4.6 其他信息通知7.4.6.1 组呼服务停止7.4.6.1.1 接口函数7.4.6.1.2 先决条件7.4.6.1.3 说明7.4.6.1.4 调用流程7.4.6.2 SAI 列表更新…

Nginx禁止文件下载防止服务器被恶意扫描

比如将网站数据库导出到站点根目录进行备份&#xff0c;很有可能也会被别人下载&#xff0c;从而导致数据丢失的风险。以下规则可以防止一些常规的文件被下载&#xff0c;可根据实际情况增减。我们可以通过以下俩种方法来防止服务器被恶意扫描&#xff0c;其中以彼之道&#xf…

ffmpeg解复用FLV文件

该博文是基于&#xff1a; ffmpeg&#xff1a;V 5.1.2 FLV格式是H2.64视频和aac音频复用得到的格式&#xff0c;我们所说的FLV解复用就是将FLV格式的数据的H2.64视频帧和aac音频帧分离。 分离完成后&#xff0c;音频是纯数据无法播放&#xff0c;需要添加ADTS头&#xff0c;视频…

浅谈基于以太网的煤矿电力监控系统的设计与应用

王兰 安科瑞电气股份有限公司 上海嘉定 201801 摘 要&#xff1a;针对传统煤矿电力监控系统通讯网络性能较差、无法实现准确故障定位及报警、不具备数据交互功能等问题&#xff0c;结合分布式网络及GPS授时技术设计了一套基于工业以太网及RS485总线架构的煤矿电力监控系…

第二十三课.扩散模型

目录概述前向过程逆向过程DDPM概述 近几年扩散模型不断涌现&#xff0c;但都来源于一个基础模型&#xff1a;DDPM&#xff08;Denoising Diffusion Probabilistic Model&#xff09;。扩散模型本质是生成模型&#xff0c;过去我们常用的生成模型包括GAN和VAE&#xff0c;利用随…

【Linux】冯诺依曼体系结构

目录&#x1f308;前言&#x1f337;1、冯诺依曼体系结构&#x1f338;2、操作系统(Operator System)&#x1f339;2.1、概念&#x1f340;2.2、如何理解管理&#x1f341;3、进程&#x1f342;3.1、概念&#x1f308;前言 本篇文章进行操作系统中进程的学习&#xff01;&…

全新一代智慧园区数字孪生解决方案,为园区运营商和集成商赋能!

对于园区运营方而言&#xff0c;园区既要满足使用者的需求&#xff0c;还要面对后期运营过程中大量、繁琐的维护管理工作&#xff0c;同时还要兼顾园区运维效率和管理成本。 随着5G、物联网、云计算、人工智能、大数据等技术的快速发展和应用&#xff0c;园区运维智能化升级已…

自动控制原理 - 2 控制系统的数学模型 节2.7-2.10

2 控制系统的数学模型2.7 结构图的等效变换准则2.8 结构图等效变换的应用2.9 信号流图2.10 梅逊公式 2 控制系统的数学模型 2.7 结构图的等效变换准则 结构图没有直接给出系统输入与输出之间的定量关系。如何得到系统输入输出之间的传递函数&#xff0c;从而便于进一步分析系…

质量管理PPAP说明

PPAP是英文Production Part Approval Process的缩写&#xff0c;中文翻译为生产件批准程序。PPAP是TS16949质量管理体系五大工具之一&#xff0c;用于汽车整车或零部件生产过程的质量管控。 各种质量管理类书籍关于PPAP的介绍有很多&#xff0c;但什么情况下必须提交PPAP&…

Spring Boot + Docker实战

文章目录1.编写DockerFile2.构建镜像3.查看编译的镜像4.启动镜像测试5.创建私有仓库5.1查询registry镜像5.2加载镜像5.3运行镜像容器5.4检查容器6.搭建Registry web6.1首先搜索并拉取镜像6.2运行一个registry web容器6.3验证7.推送镜像到私有仓库8.服务器拉取镜像部署8.1 资源限…