当前位置: 首页 > news >正文

【深入STL:C++容器与算法】深度解析string类的使用

文章目录

  • 1️⃣什么是string
    • string的设计以及编码问题
  • 2️⃣string的重要接口💫💫
    • 一、string的初始化
    • 二、string的赋值
    • 三、string的长度
    • 四、string元素获取
      • 1. char& at(size_t pos)
      • 2. operaotr []
      • 3. front和back
    • 五、迭代器
      • 1. 什么是迭代器
      • 2. 范围for底层利用迭代器
      • 3. 反向迭代器
      • 4. const迭代器
      • 5. const反向迭代器
      • 6. 四种迭代器总结
    • 六、string插入数据
      • 1. push_back--尾插字符
      • 2. append --尾插字符串
      • 3. +=运算符的重载
      • 4. append的迭代器用法
    • 七、string的容量操作
      • 1. max_size
      • 2. capacity
      • 3. 扩容机制
      • 4. reserve -- 容量预定
      • 5. resize -- 容量预定+初始化
      • 6. empty -- 判空
      • 7. clear -- 清空有效字符
    • 八、assign(赋值)
    • 九、insert(插入)
    • 十、erase(删除)
    • 十一、replace(替换)
    • 十二、c_str(返回C字符串)
    • 十三、substr(取子串)
    • 十四、find(查找)
      • 1. find -- 正向查找
      • 2. rfind -- 反向查找
      • 3. substr和find的应用:url分割
      • 4. find_first_of
    • 十五、compare(比较)
    • 十六、getline(输入string)
    • 十七、string的转换


1️⃣什么是string

关于string的介绍,标准文档是这样说的:

  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)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作

实际上string就是动态增长的char字符数组

但string其实是一个typedef之后的类模板:如下图

image-20220914094223124

string实际上是一个 模板,参数是char类型

同样,有一些少见的其他string

wstring里面存的就是wchar_t (宽字符,应用于unicode编码)image-20220914094328097

u16string:里面存的是char16_timage-20220914094507327

u32string:里面存的是char32_timage-20220914094556285

所以string其实就是basic_string类的针对char 的实例化

string的设计以及编码问题

这里就涉及编码的问题了。计算机中数据只有0和1,而如何表示各种文字呢,就要用到编码了。

常见的编码表有ASCII,GBKutf-8,utf-16,utf-32等。而编码表实际就是内存中的值 和 文字符号的映射关系,例如,字符a对应内存的值就是97

string是一个字符串,而字符串表示的是信息,所以就涉及到各种文字。我们知道ASCII码是针对于老美的,因为英美的文字只包含字母和标点符号,而一个char为8位,对于二进制可以表示256钟状态,每一个字母和标点对应一个状态对于老美绝对够用了。

但是当计算机推广的时候,其他国家的问题就不可以只依靠ASCII来编码了,比如中文的就远比英美文字复杂得多,而且中国汉字多得多,所以一个字符根本表示不完。所以中文采用多个字符的编码。常见的是两个字符即16位(65536)个状态。所以为了支持泛型编程,把string设置为一个模板,对于不同类型的字符都可以使用!

例如:

char str[]="你好";

此时str的长度就是4个字符,两个字符表示一个汉字

而每一个字符又对应一个编码

image-20220914100502572

-60-29组合起来映射的是 你
-70-61组合起来映射的是 好
//字符的编码值 对应编码表去查找

反过来验证:

char hello[] = { -60,-29,-70,-61,0 };
cout << hello << endl;

image-20220914101309800

如果让hello[3]的元素++

char str[] = "你好";hello[3]++;
cout << hello << endl;
hello[3]++;
cout << hello << endl;
hello[3]++;
cout << hello << endl;

输出结果:image-20220914104046810

发现,的周围都是hao

关于汉字的编码,可以推得:中文编码是把同音字放在一起的

平时我们打游戏的时候,一些出口即国粹的脏话输出机玩家发的消息就会变成***,而中国文化博大精深,很多谐音字也可以用来发挥作用,所以就利用汉字的编码分区,把同音字也放到一个国粹词库中了,一旦检测到是词库中的内容,就会和携成***这就是净网行动的原理。

2️⃣string的重要接口💫💫

一、string的初始化

1. string()

string s1; // s1为"\0"

​ 构造空string对象(调用默认构造函数),注意:为兼容C的字符产标准,即使为空也存在一个\0,表明这是字符串

2. string(const char* s)

string s2("hello wolrd");//s2为 "hello world"
string s3 = "hello world"; //先构造 再拷贝构造 -> 优化:直接构造

​ 这是用C的常量字符串构造一个string对象

​ 这里因为只有const char*s这一个参数,对于单参数的构造函数,可以支持隐式类型转换,即先构造再拷贝构造,一般编译器会优化为直接构造

3. string(const string& str)

string s1("hello");// s1 = "hello"
string s3(s1);//s3 = "hello"

​ 利用一个string对象构造一个对象(拷贝构造)

4.string(size_t n, char c)

string s4(5,'a'); //s4 = "aaaaa"

​ 用 n 个字符c构造一个对象

5.string(const char*s , size_t n)

string s5("hello world",5);//s5 = "hello"

​ 用 常量字符产s的前n个构造一个string对象

6. string(const string& str, size_t pos,size_t len = npos

string s1("hello wolrd");// s1 = "hello world"
string s2(s1,6,3);// s2 = "wor"
string s3(s1,6,5);// s3 = "world"
string s4(s1,6,10);// s4 = "world" 大于后面的字符数,直接拷贝到最后
string s4(s1,6); // s4 = "world"

​ 用一个string对象的pos位置开始向后len个字符来构造一个string

​ ① 如果len大于从pos位置到’\0’的长度,那么就拷贝到最后

​ ② 如果不传递len,缺省参数为 std::npos,是一个std命名空间中的全局变量,值为-1. image-20220914183429292

因为 len为size_t类型(正数),而-1为负数,二进制是32个1,赋值给len自动被转变成无符号数,约为42亿九千万,即大约是4g,而4g这么大的字符串基本上是不存在的。所以不传递len默认拷贝到最后,有多少拷贝多少

二、string的赋值

1. string& operator=(const string& str)

string s1 = "hello world";
string s2;
s2 = s1;//s2 = "hello world"

​ 用一个string进行赋值

2. string& operator=(const char*)

string s1;
s1 = "hello world";

​ 用常量字符串赋值

3. string& operator=(char c)

string s1;
s1 = 'x';//用一个字符x给s1赋值

三、string的长度

string s1 = "hello world";
cout << s1.length() << endl; //写法1
cout << s1.size() << endl; //写法2

注意:

  1. size_t size() const 返回size_t类型 (计算有效字符个数) —> 对于所有容器都有
  2. size_t length() const ----> 有的容器没有lengtn(对于树这样的,叫长度不合适)

所以一般统一用 size()

四、string元素获取

1. char& at(size_t pos)

0.

顾名思义,函数返回pos位置的元素的引用

普通对象调用char& at(size_t pos)

const对象调用const char& at(size_t pos) const

operator[]的区别就是,如果pos超出string的索引范围(越界),会抛异常,还可以继续执行后面的程序。operator[]是断言直接终止

2. operaotr []

image-20220914185447685

两个函数构成函数重载

普通对象调用char& operator[] (size_t pos)

const对象调用const char& operator[] (size_t pos) const;

string s1 = "hello world";//char& operator[] (size_t pos);
cout << s1[0] << endl;//用[]进行访问
s1[0] = 'x';//用[]修改原字符
cout << s1 << endl;//const char& operator[] (size_t pos) const;
const string s2 = "const string";
//s2[0] = 'd';//const类型不能修改
cout << "s2[0]:" << s2[0] << endl;
//cout << s2[15] << endl;; 超出s2的长度,报错:assertion failed

利用[]遍历string

string s3 = "string";
const string s4 = "const string";
//遍历s3
for (size_t i = 0; i < s3.length(); ++i)
{s3[i]++;
}
cout << s3 << endl;//利用[]修改
for (size_t i = 0; i < s3.length(); ++i)
{s3[i]++;
}//const类型只能遍历,不能修改
const string s4 = "const string";
for (size_t i = 0; i < s4.size();++i)
{cout << s4[i] << " ";//cout << s4.at(i) << " "; 两种效果一样
}
cout << endl;

3. front和back

image-20220914222127296

image-20220914222154329

front是返回第一个字符

back返回最后一个字符(不算’\0’ )

五、迭代器

1. 什么是迭代器

迭代器是一个类型iterator

每一个数据结构内部都有定义的这么一个类。如string::iterator就是string的迭代器,vector::iterator就是vector的迭代器

迭代器是一个像指针一样的类型,可能是指针,也可能不是指针,但是用法像指针,具体细节封装了起来。

对于string和vector其实就是原生指针,但是对于list就不是指针了,而是一个自定义类型

我们知道可以利用[]遍历string,但是[]只针对底层利用数组实现的类型,对于其他一些比如链表、树型结构就不能利用[]遍历了。

iterator是对于STL容器的统一遍历方法,用法都是类似的,不管是顺序表还是链式表,使用都是一样的~

对于迭代器iterator,string中有两个常用的函数,返回类型是迭代器类型

begin()end(),分别返回第一个位置的迭代器和最后一个位置的下一个位置的迭代器,[begin,end),左闭右开!

image-20220915103828127

如图所示,类似指针,可以利用迭代器去遍历string!

image-20220915102638629

string s("hello");
//利用迭代器去遍历
//定义一个迭代器类型 it
string::iterator it = s.begin();//it为第一个位置的迭代器
while (it != s.end())//当it迭代到最后一个位置的时候结束
{cout << *it << " "; //类似指针解引用++it;
}
cout << endl;//const迭代器无法修改(类似const指针)
string s1 = "hello world";
string::const_iterator it3 = s1.begin();
while (it3 != s1.end())
{cout << *it3 << " ";it3++;//(*it3)++; 不能修改
}
cout << endl;

同理,迭代器可以进行修改,和指针类似的用法(*it)++

但是对于string和vector类型很少用迭代器,因为底层用数组实现,[]使用起来更香,对于list、map等容器必须使用迭代器遍历

2. 范围for底层利用迭代器

范围for可以遍历任何容器,并具有自动迭代,自动判断结束的功能,十分智能。

注意,范围for只能正向遍历,不能反向

string s = "hello";
for (auto i : s)//依次取s中的每一个元素赋值给i 
{cout << i << " ";
}
cout<<endl;//利用范围for修改元素
for (auto& i : s) //依次给s中每一个元素取别名
{i++;cout << i << " ";
}

其实范围for的底层就是迭代器,编译器会自动把范围for替换成迭代器

迭代器遍历的汇编:

image-20220915112340178

范围for的汇编

image-20220915112300258

所以,从汇编指令的角度可以看到,范围for就是去调用了迭代器进行遍历

3. 反向迭代器

类名::reverse_iterator,如string::reverse_iterator

反向迭代器就是从后面向前迭代,利用两个函数

image-20220915121850421

rbegin():返回反向的第一个位置的迭代器(正向的最后一个位置)

rend():返回反向的最后一个位置的下一个位置的迭代器(正向第一个位置的前一个位置)

image-20220915122444904

string s = "hello";//反向迭代器
string::reverse_iterator r_it = s.rbegin();//必须用反向迭代器接受 rbegin()的返回结果
while (r_it != s.rend())
{cout << *r_it << " ";++r_it; //注意这里也是++ 不是--
}
cout << endl;//输出 o l l e h 

4. const迭代器

const迭代器:const_iterator,如string::const_iterator

const迭代器 – 经常发生在传参过程中(传参经常传const 对象&)

一般利用begin()end()的返回值,当然也可以使用cbegin()cend()

cbegin()和cend()是C++11支持的,只是为了更规范一些,

因为begin()end()是具有重载的,根据是否为const决定是否调用哪一个重载,考虑到有些时候可能难以看出调用的是哪一个重载,才引入了cbegin()cend(),但其实没有什么必要,用begin()end()就可以了

使用情景:

  1. 对象是const
  2. 自定义对象一般传递const 引用
//传参是 const引用       str = "hello"
void PrintString(const string& str)
{//这时候打印str// string::iterator c_it = str.begin() -> 错误:str调用begin返回const迭代器,要用const迭代器接收//因为str为const类型,所以迭代器要用conststring::const_iterator c_it = str.begin();//或str.cbegin() C++11支持 [然并卵]while (c_it != str.end()){//(*c_it) = 'x';//const!不能修改cout << *c_it << " ";++c_it;}cout << endl;
}//输出 h e l l o

5. const反向迭代器

类名::const_reverse_iterator

利用函数:rbegin()rend()

或者crbegin()crend() ->C++11推出,与cbegin(),cend()都是为了规范性,没什么使用的必要

//str = "hello"
void PrintString(const string& str)
{//反向打印strstring::const_reverse_iterator c_r_it = str.rbegin();//调用rbegin()while (c_r_it != str.rend()){cout << *c_r_it << " ";++c_r_it;}cout << endl;
}//输出 o l l e h 

6. 四种迭代器总结

以string类为例

迭代器函数
string::iteratorbegin(),end()
string::reverse_iteratorrbegin(),rend()
string::const_iteratorbegin(),end() 或cbegin(),cend()
string::const_reverse_iteratorrbegin(),rend()或crbegin(),crend()

六、string插入数据

1. push_back–尾插字符

即在字符串的尾部插入一个字符

//void push_back(char c)--(插入字符)
string s = "hello";
s.push_back('-');//s变成:"hello-"

2. append --尾插字符串

//append() ----插入字符串
string s = "hello"
s.append("world");//s变成:"helloworld"

3. +=运算符的重载

string类实现了+=的运算符重载,从而实现字符串的拼接功能

同时,有三个函数重载:

string& operator+= (const string & str):拼接string
string & operator+= (const char* s):拼接C字符串
string& operator+= (char c):拼接一个字符

string s1("good");
s1 += " evening ";//+= const char*
s1 += '@';// += char
string s2(" my friend");
s1 += s2;//+= string对象 
cout << s1 << endl;/************************************/
// s1 : good evening@ myfriend

其实+=的底层就是调用了push_back和append

相比C语言,C++字符串的操作是简单了很多

  1. 定义一个字符串,要么就是定义字符数组char[],但是数组的容量又是一个问题,开大开小都不合适,如果是动态开辟还要检查扩容
  2. 拼接字符串,C语言追加的strcat函数效率低并且不管空间,必须调用者保证好空间,该函数只管追加。而string的+=重载里有size记录字符串长度,并且还会自动判断扩容。

4. append的迭代器用法

使用迭代器可以规定append的范围

主要用处是把某字符串的一部分追加到原字符串的后面

string str = "hello world";
string s = "my friend";
str.append(s.begin(),s.end());//等价于str+=s
//从第二个位置的迭代器开始,倒数第二个位置结束(不追加第一个字符和最后一个字符)
str.append(++s.begin(),--s.end());//str = "hello worldy frien"

七、string的容量操作

1. max_size

string的最大长度,是写死的,其值为整形的最大值(32个1)没啥意义

string s;
string s1("hello world");
cout << s.max_size() << endl;
cout << s1.max_size() <<  endl;//同一环境,max_size()是一个定值

2. capacity

获取string对象容量大小(不包括’\0’)

string s;
size_t sz = s.capacity();
cout << "init capacity:" << sz << endl;//输出 15 (默认给15大小)
// 意思就是 有15个可以存储有效字符的空间
// '\0'是标识字符不是有效字符

3. 扩容机制

  1. Windows Visual Stdio2019下的扩容机制

    //测试:尾插1000个x会扩容几次
    int main()
    {string s;size_t sz = s.capacity();//初始cout << "init capacity:" << sz << endl;cout << "grow" << endl;for (int i = 0; i < 1000; i++){s.push_back('x');//如果发生扩容,就打印改变后的容量if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed:" << sz << endl;}}return 0;
    }
    

    结果:image-20220915163306604

    可以看到,VS下初始容量为15,并且刚开始是2倍扩容,后面约是1.5倍扩容

  2. Linux下的扩容机制,直接看运行截图:

    image-20220915164400914

    可以发现Linux下初始容量为0,并且始终是2倍扩容

4. reserve – 容量预定

有时候需要开一定的空间,但是如果空间比较大就需要频繁扩容

不过reserve()这个成员函数可以提前开好空间(只改变capacity),减少频繁扩容(如果知道需要多少空间的情况下),单位是字节

    /* reserve -- 对容量进行预留(如有需要可以提前开好,减少扩容)*/string s1;s1.reserve(1000);//提前开好1000的容量(考虑对齐VS下是1007个有效空间)Linux下1000size_t sz1 = s1.capacity();cout << "init capacity:" << sz1 << endl;cout << "grow" << endl;for (int i = 0; i < 1000; i++){s1.push_back('x');if (sz1 != s1.capacity()){sz1 = s1.capacity();cout << "capacity changed:" << sz1 << endl;}}

5. resize – 容量预定+初始化

image-20220915165447510

resize改变的是string的长度,如果容量不够也会扩容,并且会用字符去初始化。size和capacity会同时改变

string s;
s.resize(100);//开100个空间,没给参数用'\0'初始化
s.resize(100,'x');// 给参数为'x',用'x'初始化//但是size和capacity都会变成100 所以还会扩容

注意,如果字符串本身已经有内容了,那么resize只是会起到追加的作用,不会影响原来的字符串

6. empty – 判空

string str1 = "hello";
string str2;
cout<<str1.empty()<<endl; // 输出 0
cout<<str2.empty()<<endl; // 输出 1

7. clear – 清空有效字符

string str = "welcome to my blog";
str.clear();		// str = "\0"  即变成空字符

八、assign(赋值)

assign类似直接给字符串赋值,不过用的很少

string str;
string temp = "this is an example";//str.assign (size_t n, char c);	将n个字符c赋值给字符串str
str.assign(10,'x');						// str = "xxxxxxxxxx"//str.assign(const string& s)		将字符串s赋值给str
str.assign(temp);					//str = "this is an example"//str.assign(const string& s, size_t pos, size_t len)		将字符串的s从pos位置开始的len个字符赋值给字符串str
str.assign(temp,6,2);		//str = "is"//assign (const char* s)		将字符数组s赋值给str
str.assign("hello");			//str = "hello"//assign(const char*s,size_t n)		将字符数组的前n个字符赋值给str
str.assign("hello",3);				//str = "hel"

九、insert(插入)

insert也不是很常用,简单了解几个常用的即可

string temp = "hello world";//insert(size_t pos,const string& str)		从pos位置插入字符串str
string s1 = "xxx";
temp.insert(5,s1);							// str = "helloxxx world"//insert(size_t pos,const char* str)		从pos位置插入字符数组str
temp.insert(5,"ggg");						// str = "helloggg world"//insert(iterator p,charc)					从迭代器p位置插入一个字符c
temp.insert(str.begin(),'5');				// str = "5hello world"

十、erase(删除)

string str = "welcome to my house";
//erase (pos = 0,len = npos)		删除pos位置开始的len个字符,pos默认为0,len默认为无限大(删到结尾
str.erase(14,5);					//str = "welcome to my "
str.erase(7);						//str = "welcome"
str.erase();						//str = ""; //空字符串//erase (iterator p);				//删除p位置迭代器的字符
str.erase(str.begin()+2);			//str = "wecome to my house"//erase (iterator first, iterator last)  //删除迭代器区间[first,last)的字串
str.erase(str.begin()+7,str.end()-5);	 //str = "welcomehouse"

十一、replace(替换)

替换的部分长度可以不一样,类似于先删除原部分,然后再插入

效率很低,用的不多

//replace (size_t pos,  size_t len,  const string& str);	源字符串从pos位置往后的len个字符用str替换
string s1 = "hello the world";
string s2 = "m"
s1.replace(6,3,"great"); 			// s1 = "hello great world"//replace (size_t pos,  size_t len,  const char* s)			源字符串从pos位置往后的len个字符用C字符串s替换
s1.replace(105"string");		   // s1 = "hello the string"//replace (size_t pos,  size_t len,  const char* s, size_t n);		原字符串从pos位置往后len个字符用C字符串s的前n个字符替换
s1.replace(10,5,"cpp is good",3);	// s1 = "hello the cpp"

十二、c_str(返回C字符串)

因为C的字符串是char*类型,不同于string

并且有些场景是存在需要返回char*类型的,不可以返回string类型.因为C++兼容C,很多底层一些接口是用C写的,这样二者都可以使用

比如用C的形式打开文件

const char* c_str() const 函数的作用就是把string类型变成const char*类型来使用,底层就是返回了string封装的char*成员

string filename("test.cpp");
//FILE* pf = fopen(filename,"r");	//error		类型不匹配
FILE* pf = fopen(filename.c_str(),"r"); 	//  √
if(pf==NULL)
{perror("fopen fail");exit(-1);
}
char c = fgetc(pf);
while(c!=EOF)
{cout<<c;c = fgetc(pf);
}

★★★注意,二者打印的原理是不同的★★★

**string是以size() 为结束标志 **

string.c_str是以’\0’为结束标志

string str = "hello world";
cout << str << endl;    //调用 string的 operator << 重载
cout << str.c_str() << endl;	// 调用const char* 的 operator << 重载
// 上面输出是一样的str += '\0';
str += 'welcome';cout << str <<endl;			    //输出:  hello world welcome
cout << str.c_str() << endl;	//输出:  hello world//因为c_str以'\0'为标志就结束了!,不会打印'\0'后面的内容
//所以,C字符串其实是有bug的,C字符串里面不能夹杂着'\0'

image-20220916112307579

'\0'是不显示字符,所以打印就会显示一个空白

十三、substr(取子串)

image-20220916132525489

即:返回从原字符串的pos位置处向后的len个字符的子串

默认pos为0,len默认为整形最大值(无限大),

string str = "hello my friend";
string s1 = str.substr(6,2);		//s1 = "my"

十四、find(查找)

1. find – 正向查找

find函数如果找到返回第一个位置的下标,找不到返回string::npos

string str = "hello world";
//find (const string& str, size_t pos = 0) const		从pos位置处查找子串str(返回第一个位置)
string substr = "wor";
size_t pos1 = str.find(substr);				//pos1 = 6//find(const char* s,size_t pos = 0) const 		从pos位置查找C字符串s
size_t pos2 = str.find("ld");				//pos2 = 9//find(char c,size_t pos = 0)const 				从pos位置查找字符c
size_t pos3 = str.find('e');				//pos3 = 1//find(const char* s,size_t pos,size_t n)		从pos位置查找c字符串s的前n个   
size_t pos4 = str.find("hello",0,3);		//pos4 = 0 

2. rfind – 反向查找

rfind和find 用法相同,只是rfind是查找最后一次出现的位置

rfind的应用:找文件名后缀(Linux下文件名需要是最后一个.后面才是后缀名)

string filename = "test.cpp.tar.zip";		//后缀名是.zip
size_t pos = filename.rfind('.');			//找最后一个.
//如果找到
if(pos != string::npos)
{cout<<filename.substr(pos);//找到就打印pos后面的字串	
}//输出:.zip

3. substr和find的应用:url分割

对一个网址进行url分割,url一般格式为协议://域名/资源定位符

所以利用find()substr(),找"://“和它后面的第一个/就可以了

string url = "https://legacy.cplusplus.com/reference/string/string/";
size_t pos1 = url.find("://");
if (pos1 == string::npos)
{cout << "url不合法" << endl;
}
string protocol = url.substr(0,pos1);//协议:第一个字符到pos1之间 共pos1个字符
size_t pos2 = url.find('/', pos1 + 3);
if (pos2 == string::npos)
{cout << "url不合法" << endl;
}
string domain = url.substr(pos1 + 3, pos2 - pos1 - 3);//域名:"://"后面的第一个字符到下一个'\'之前 pos2-pos1-3个字符       任意(pos1,pos2)之间有pos2-pos1+1个字符//剩下的到最后就是资源定位符
string uri = url.substr(pos2+1); //pos2+1到最后即可
cout << protocol << endl;
cout << domain << endl;
cout << uri << endl;

image-20220916163019742

4. find_first_of

find是找到一个完全匹配的子串

find_first_of是只要找到目标串中的一个字符,就返回当前的位置

string str = "please notice that i am a sentence";
size_t pos = str.find_first_of("aeiou");	//找出元音字母的字符,并改变为*
//只要还能找到就继续循环						// 只要找到"aeiou"其中的一个,就返回
while(pos!=string::npos)
{str[pos]='*';pos = str.find_first_of("aeiou",pos+1);	//从pos的下一个位置开始继续找
}
cout << str << endl;//输出:pl**s* n*t*c* th*t * *m * s*nt*nc*

find_last_of和find_first_of类似,不同的是find_last_of是从后往前找(了解)

十五、compare(比较)

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

string str1 = "abcdef";
string str2 = "abcefg";//s.compare(str)                     比较当前字符串s和str的大小
cout << str1.compare(str2);                   // -1 (str2 > str1)//s.compare(pos,n,str)               比较当前字符串s从pos开始的n个字符与str的大小
cout << str1.compare(1,5,str2);				  // 1  (b>a)//string也支持和const char*类型比较

十六、getline(输入string)

我们通常使用的 cin>>可以获取字符串,但是cin>>遇到空格或者换行就会停止输入

所以,如果要输入包含空格的一个句子,就不可以用cin>>来获取输入了

需要用到getline():获取一行,只有遇到换行的时候才会停止输入

image-20220916173821646

is 中提取字符并将其存储到 str 中,直到找到分隔字符 delim(如果不给就是换行符“\n”

如果到达文件的末尾*,*或者在输入操作期间发生其他错误,则提取也会停止。

如果找到分隔符,则将其提取并丢弃(即不存储它,下一个输入操作将在它之后开始)

string s;
getline(cin,s);	//输入 i am a student
cout<<s<<endl;	//输出:i am a studentstring str;
getline(cin, str,'x');			//如果不输入x  就一直提示你输入
cout << str << endl; 			//输入x之后,停止并输出

十七、string的转换

//其他类型转字符串
to_string(val);		//val转化为string对象,返回类型:string,val可以是int、double、float、long等等int a = 123456;
string str = to_string(a);
cout << str <<endl;			// 123456double dd = 12.34;
string ddd = to_string(dd);
cout << ddd << endl;		//12.340000  (默认小数点后6位)

关于stoi(字符串转整形)

int stoi(const string& str, size_t* idx= 0, int base = 10 )

第一个参数是要转的字符串

第二个参数是一个size_t类型数据的指针,传指针是为了改变实参,(传进去之后,函数会把同时包含数字和字符的字符串中最后一个数字的下一个位置的字符位置给它),只能是数字在前面的情况才会给你

第二个参数一般可以不传递

第三个参数是要转换成的进制,默认为10进制

//字符串转其他类型
string str = "2008,a good year";
size_t pos = 0; 					//用于记录数字后字符的位置
int a = stoi(str, &pos, 10);		
cout << a << endl;					//	2008
cout << str.substr(pos)<<endl;		//  ,a good yearstring str1 = "13524ddd";
int b = stoi(str1);					//	一般使用只需传递字符串即可
cout<<b<<endl;						//	13524//stod -- 字符串转double
string s = "9999.99";
double d = stod(s);
cout<<d<<endl;						//9999.99

相关文章:

【深入STL:C++容器与算法】深度解析string类的使用

文章目录 1️⃣什么是stringstring的设计以及编码问题 2️⃣string的重要接口&#x1f4ab;&#x1f4ab;一、string的初始化二、string的赋值三、string的长度四、string元素获取1. char& at(size_t pos)2. operaotr []3. front和back 五、迭代器1. 什么是迭代器2. 范围fo…...

【ChatGPT】解锁AI思维链:如何让机器像人类一样思考?

在人工智能领域&#xff0c;我们一直在追求让机器像人类一样思考。然而&#xff0c;即使是最先进的AI&#xff0c;也常常被诟病缺乏“常识”&#xff0c;难以理解复杂问题&#xff0c;更不用说像人类一样进行逻辑推理和解决问题了。最经常的表现就是遇到不会的地方&#xff0c;…...

用 Python 从零开始创建神经网络(十七):回归(Regression)

回归&#xff08;Regression&#xff09; 引言1. 线性激活&#xff08;Linear Activation&#xff09;2. 均方误差损失&#xff08;Mean Squared Error Loss&#xff09;3. 均方误差损失导数&#xff08;Mean Squared Error Loss Derivative&#xff09;4. 平均平方误差 (MSE) …...

gentoo安装Xfce桌面

一、安装Xfce 1.选择一个配置文件 具体步骤可参见笔者的另一篇博客https://blog.csdn.net/my1114/article/details/143919066&#xff0c;配置文件选择24. 2.安装Xfce (1)root #emerge --ask xfce-base/xfce4-meta 第一次启动登录后时可能还需starx来启动X11 (2)安装slim&#…...

阿尔茨海默症数据集,使用yolo,voc,coco格式对2013张原始图片进行标注,可识别轻微,中等和正常的症状

阿尔茨海默症数据集,使用yolo&#xff0c;voc&#xff0c;coco格式对2013张原始图片进行标注&#xff0c;可识别轻微&#xff0c;中等&#xff0c;严重和正常的症状 数据集分割 训练组100&#xff05; 2013图片 有效集&#xff05; 0图片 测试集&#xf…...

【物联网技术与应用】实验4:继电器实验

实验4 继电器实验 【实验介绍】 继电器是一种用于响应施加的输入信号而在两个或多个点或设备之间提供连接的设备。换句话说&#xff0c;继电器提供了控制器和设备之间的隔离&#xff0c;因为设备可以在AC和DC上工作。但是&#xff0c;他们从微控制器接收信号&#xff0c;因此…...

lvs介绍与应用

LVS介绍 LVS&#xff08;Linux Virtual Server&#xff09;是一种基于Linux操作系统的虚拟服务器技术&#xff0c;主要用于实现负载均衡和高可用性。它通过将客户端请求分发到多台后端服务器上&#xff0c;从而提高整体服务的处理能力和可靠性。lvs是基于集群的方式实现 集群…...

Group FLUX - User Usage Survey Report

文章目录 User Feedback Summary: Software Advantages and FeaturesUser Feedback Issues and Suggested Improvements1. Security Concerns:Improvement Measures: 2. System Performance and Loading Speed:Improvement Measures: 3. Data Display Issues:Improvement Measu…...

XXE靶机攻略

XXE-Lab靶场 1.随便输入账号密码 2.使用bp抓包 3.插入xxl代码&#xff0c;得到结果 xxe靶机 1.安装好靶机&#xff0c;然后输入arp-scan -l&#xff0c;查找ip 2.输入ip 3.使用御剑扫描子域名 4.输入子域名 5.输入账号密码抓包 6.插入xml代码 7.使用工具解码 8.解码完毕放入文…...

第78期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…...

电容Q值、损耗角、应用

电容发热的主要原因&#xff1a;纹波电压 当电容两端施加纹波电压时&#xff0c;电容承受的是变化的电压&#xff0c;由于电容内部存在寄生电阻&#xff08;ESR&#xff09;和寄生电感&#xff08;ESL&#xff09;.因此电容会有能量损耗&#xff0c;从而产生热量&#xff0c;这…...

【WRF教程第3.6期】预处理系统 WPS 详解:以4.5版本为例

预处理系统 WPS 详解&#xff1a;以4.5版本为例 Geogrid/Metgrid 插值选项详解1. 插值方法的工作机制2. 插值方法的详细说明2.1 四点双线性插值&#xff08;four_pt&#xff09;2.2 十六点重叠抛物线插值&#xff08;sixteen_pt&#xff09;2.3 简单四点平均插值&#xff08;av…...

linux 安装redis

下载地址 通过网盘分享的文件&#xff1a;redis-7.2.3.tar.gz 链接: https://pan.baidu.com/s/1KjGJB1IRIr9ehGRKBLgp4w?pwd0012 提取码: 0012 解压 tar -zxvf redis-7.2.3.tar.gz mv redis-7.2.3 /usr/local/ cd /usr/local/redis-7.2.3 安装 make install 修改配置文件 /搜索…...

Linux - rpm yum 工具及命令总结

RPM 概述 定义&#xff1a;RPM&#xff08;RedHat Package Manager&#xff09;&#xff0c;是一个功能强大的软件包管理系统&#xff0c;用于在 Linux 系统中安装、升级和管理软件包采用系统&#xff1a;主要用于基于 RPM 的 Linux 发行版&#xff0c;如 Red Hat、CentOS、S…...

电子应用设计方案-58:智能沙发系统方案设计

智能沙发系统方案设计 一、引言 智能沙发作为一种融合了舒适与科技的家居产品&#xff0c;旨在为用户提供更加便捷、舒适和个性化的体验。本方案将详细介绍智能沙发系统的设计思路和功能实现。 二、系统概述 1. 系统目标 - 实现多种舒适的姿势调节&#xff0c;满足不同用户的…...

复习打卡Linux篇

目录 1. Linux常用操作命令 2. vim编辑器 3. 用户权限 4. Linux系统信息查看 1. Linux常用操作命令 基础操作&#xff1a; 命令说明history查看历史执行命令ls查看指定目录下内容ls -a查看所有文件 包括隐藏文件ls -l ll查看文件详细信息&#xff0c;包括权限类型时间大小…...

在Ubuntu 22.04 LTS中使用PyTorch深度学习框架并调用多GPU时遇到indexSelectLargeIndex相关的断言失败【笔记】

在Ubuntu 22.04 LTS系统中&#xff0c;已安装配置好CUDA 12.4、cuDNN 9.1.1以及PyTorch环境 export CUDA_VISIBLE_DEVICES0,1,2,3,4,5,6,7 在PyTorch深度学习框架训练调用多GPU时&#xff0c;提示 indexSelectLargeIndex: block: [x, 0, 0], thread: [x, 0, 0] Assertion src…...

qt 类中的run线程

在Qt中&#xff0c;QThread类的run()方法是线程的执行入口&#xff0c;它是由QThread内部自动调用的&#xff0c;而不是用户直接调用。 详细解释&#xff1a; QThread类&#xff1a; QThread是Qt的线程类&#xff0c;提供了用于多线程操作的接口。我们可以创建QThread对象并将…...

Vue3父子组件传属性和方法调用Demo

Vue3父子组件传属性和方法调用Demo 说明目录父组件给子组件传值和方法 父组件给子组件传值-使用defineProps接受父组件属性值父组件给子组件传值-使用defineModel接受父组件v-model值 当子组件只需要接收父组件一个v-model值时,写法1如下:子组件接收单个v-model写法2如下:当子…...

aac怎么转为mp3?操作起来很简单的几种aac转mp3的方法

aac怎么转为mp3&#xff1f;aac格式的优势主要体现在音质和压缩效率&#xff0c;尤其是在较低比特率下&#xff0c;能够实现更清晰的音质&#xff0c;这也是为何许多现代设备和应用偏爱aac格式的原因之一。特别是在手机、平板以及智能音响等设备中&#xff0c;aac文件几乎可以无…...

结合mybatis-plus实现Function获取java实体类的属性名

1、工具类 package com.yh.tunnel.util;import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.google.common.base.CaseFormat; import com.yh.tunnel.domain.Plan;import java.lang.invoke.SerializedLambda; import java.lang.reflect.Field; import…...

vue 响应式数据原理

发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【宝藏入口】。 Vue 的响应式数据原理是其核心功能之一&#xff0c;它使得 Vue 应用能够自动响应数据的变化&#xff0c;并在数据变化时自动更新…...

android 计算CRC

<?php /** * 将一个字符按比特位进行反转 eg: 65 (01000001) --> 130(10000010) * param $char * return $char */ function reverseChar($char) { $byte ord($char); $tmp 0; for ($i 0; $i < 8; $i) { if ($byte & (1 << $i)…...

Linux tinyproxy 使用教程

简介 Tinyproxy 是一款轻量级 HTTP 代理服务器&#xff0c;使用最少的资源&#xff0c;非常适合硬件有限的系统。尽管体积小&#xff0c;但它可以处理大量流量&#xff0c;而不会出现明显的性能问题。旨在处理简单的代理任务。它通常用于路由网络流量以保护隐私、缓存或访问受…...

局部规划器设计思路

本文参考知乎文章:如何设计局部规划器 0 引言 局部规划器设计通用方法:生成路径——>寻找最优路径——>后处理优化 1 路径生成 四个问题: ① 如果全局路径中突然出现动态障碍物 ② 如果全局路径非常靠近障碍物 ③ 如果全局路径不容易跟踪(B样条平滑) ④ 如果全局…...

数字图像处理技术期末复习

1. 已知图像的分辨率和深度&#xff0c;怎么求图像的存储空间&#xff08;位&#xff0c;字节&#xff0c;KB&#xff09;&#xff1f; 题目&#xff1a; 已知图像的分辨率和深度&#xff0c;怎么求图像的存储空间&#xff08;位&#xff0c;字节&#xff0c;KB&#xff09;&a…...

UITableView显示数据,增加数据,删除数据及移动数据行

UITableView和html中的table有点类似的&#xff0c;也有header和footer和body&#xff0c;row。下面给出一个demo // // TableViewTestViewController.m // iosstudy2024 // // Created by figo on 2024/12/9. //#import "TableViewTestViewController.h"interfa…...

金智塔科技喜获CCF中国数字金融大会 GraphRAG竞赛二等奖

12月7日&#xff0c;CCF 首届中国数字金融大会GraphRAG竞赛在上海落下帷幕&#xff0c;金智塔科技&#xff08;团队名称&#xff1a;塔塔向前冲&#xff09;从众多参赛队伍中脱颖而出&#xff0c;喜获二等奖。 CCF 首届中国数字金融大会由中国计算机学会主办&#xff0c;中国计…...

方案解读:数字化扩展中如何提升多云应用安全能力?

越来越多企业选择上云&#xff0c;拥抱数字化转型。数据显示&#xff0c;在过去一年中&#xff0c;将应用托管至六种不同环境中的企业比例已经翻倍&#xff0c;达到令人震惊的38%。与此同时&#xff0c;应用和流经其的关键数据已成为日益复杂的网络攻击的首选目标&#xff0c;且…...

“年轻科技旗舰”爱玛A7 Plus正式发布,全国售价4999元

12月18日&#xff0c;备受行业瞩目的“A7上场 一路超神”爱玛旗舰新品发布会在爱玛台州智造工厂盛大举行。 作为年末“压轴产品”的“两轮豪华轿跑”爱玛A7Plus重磅上场&#xff0c;以“快、稳、帅、炫、智、爽”六大超神技惊艳四座&#xff0c;不仅践行了爱玛科技的精品战略&…...