wordpress可以做电影网站吗,西安市住房和城乡建设局门户网站,常熟做网站的,做网站怎么备案假如没有给你生命#xff0c;你连失败的机会都没有。你已经得到了最珍贵的#xff0c;还需要抱怨什么!#x1f493;#x1f493;#x1f493; 目录 ✨说在前面
#x1f34b;知识点一#xff1a;什么是string#xff1f;
•#x1f330;1.string类的概念
•#x1… 假如没有给你生命你连失败的机会都没有。你已经得到了最珍贵的还需要抱怨什么! 目录 ✨说在前面
知识点一什么是string
•1.string类的概念
•2.string类的主要特点
•3.常用接口说明
知识点二string类常用接口
•1.默认成员函数
构造函数⭐
析构函数
•2.string类对象的访问和遍历操作
operator[ ]⭐ 迭代器⭐
•3.string类对象的容量操作
size、length、capacity
reserve⭐
clear
empty
resize
•4.string类对象的修改操作 push_back、append
operator
assgin
insert⭐
erase
replace
swap
pop_back
•5.string类对象的其他操作
c_str
find
rfind
substr⭐
find_first_of
find_last_of
find_first_not_of
find_last_not_of
•6.string类的非成员函数
operator
relational operators
getline
•7.例题训练
题目一仅仅反转字母
题目二第一个唯一字符
题目三字符串相加 • ✨SumUp结语 ✨说在前面 亲爱的读者们大家好我们又见面了上一篇文章我给大家介绍了一下STL是什么以及相关的一些历史和容器介绍。如果大家没有掌握好相关的知识上一篇篇文章讲解地很详细可以再回去看看复习一下再进入今天的内容。 我们今天简单给大家讲解一下C标准库中一个非常重要的组成部分——string字符串。如果大家准备好了那就接着往下看吧~ 知识连线时刻直接点击即可 【C】STL简介 复习回顾 博主主页传送门愿天垂怜的博客 知识点一什么是string •1.string类的概念
C语言中字符串是以\0结尾的一些字符的集合为了操作方便C标准库中提供了一些str系列的库函数但是这些库函数与字符串是分离开的不太符合OOP的思想而且底层空间需要用户自己管理稍不留神可能还会越界访问。
于是 C 中就引入了string类它可以看做是一个管理字符串的数据结构。 C中的string类是一个非常强大的类它提供了对字符串的丰富操作。string类是C标准模板库STL的一部分定义在头文件string中。这个类封装了字符数组的功能并且添加了许多便利的成员函数来操作字符串如查找、替换、插入、删除等。
•2.string类的主要特点
动态内存管理
string类自动管理内存当你修改字符串的大小时它会自动调整内存分配。
丰富的成员函数
提供了大量的成员函数来执行各种字符串操作如长度获取length()或size()、子串提取substr()、字符串比较compare()、查找find()、替换replace()、插入insert()、删除erase()等。
安全
使用string类比直接使用字符数组更安全因为它避免了缓冲区溢出的风险。
标准库支持
作为STL的一部分string类得到了广泛的支持
•3.常用接口说明
string类对象的常见构造 (constructor) 函数名称 功能说明 string() 重点 构造空的string类对象即空字符串 string(const char* s) 重点 用C-string来构造string类对象 string(size_t n, char c) string类对象中包含n个字符c string(const strings) 重点 拷贝构造函数 string类对象的容量操作 函数名称 功能说明 size重点 返回字符串有效字符长度length返回字符串有效字符长度capacity返回空间总大小empty 重点检测字符串释放为空串是返回true否则返回falseclear 重点 清空有效字符 reserve 重点 为字符串预留空间 * * resize 重点将有效字符的个数该成n个多出的空间用字符c填充
string类对象的访问及遍历操作
函数名称功能说明 operator[] 重 点 返回pos位置的字符const string类对象调用begin end begin 获取一个字符的迭代器 end 获取最后一个字符下一个位 置的迭代器 rbegin rend begin 获取一个字符的迭代器 end 获取最后一个字符下一个位 置的迭代器 范围forC11支持更简洁的范围for的新遍历方式 string类对象的修改操作
函数名称功能说明push_back在字符串后尾插字符cappend在字符串后追加一个字符串 operator ( 重 点 ) 在字符串后追加字符串strc_str(重点)返回C格式字符串 find npos ( 重 点 ) 从字符串pos 位置开始往后找字符 c返回该字符在字符串中的位置 rfind 从字符串pos 位置开始往前找字符 c返回该字符在字符串中的位置 substr在str中从pos位置开始截取n个字符然后将其返回 string类非成员函数
函数功能说明operator尽量少用因为传值返回导致深拷贝效率低operator 重点输入运算符重载operator 重点输出运算符重载getline 重点获取一行字符串relational operators 重点大小比较 知识点二string类常用接口 •1.默认成员函数
构造函数⭐
接口如下前面也有大家再仔细看看 在文档中我们可以查看它们的具体用法 我们大家也需要学会查看英文文档有不懂的就去查锻炼我们查看英文文章的能力。
#include iostream
using namespace std;
#include stringint main()
{string s1;cout s1 endl;string s2(hello world);cout s2 endl;string s3(s2);cout s3 endl;string s4(s2, 6, 15);cout s4 endl;string s5(s2, 6);cout s5 endl;string s6(hello wrold, 5);cout s6 endl;string s7(10, x);cout s7 endl;return 0;
}
析构函数
析构函数直接用编译器默认生成、调用的就可以了非常简单~
•2.string类对象的访问和遍历操作
operator[ ]⭐
operator[ ]允许你像访问数组元素一样访问字符串中的字符operator[ ]返回的是对字符串中字符的引用char*你可以读取也可以修改该位置的字符。 void test_string1()
{string s1;string s2(hello world);cout s1 s2 endl;s2[0] x;cout s1 s2 endl;
} 如上面的代码我们可以直接像访问数组一样将s2的第一个元素修改为x本质是通过operator[]函数。
我们后面会学习【size】这个接口可以获取字符串的有效长度那么我们要遍历这个字符串就变得很简单方便
for (size_t i 0; i s1.size(); i)
{cout s1[i] ;
}
cout endl;
我们还可以用迭代器的方式遍历这个字符串
string::iterator it s2.begin();
while (it ! s2.end())
{cout *it ;it;
}
cout endl;
那这是什么意思呢我们可以将it想象成一个指针但不一定是指针begin()会返回第一个位置的迭代器而end()是\0位置的迭代器这样就很容易理解了。 所有的容器都可以用这种类似的方式进行遍历例如链表
listint lt { 1,2,3,4,5,6,7 };
listint::iterator lit lt.begin();
while (lit ! lt.end())
{cout *lit ;lit;
}
cout endl; 这是第二种遍历的方式那么我们还有第三种这是C11新增的
for (auto ch : s2)
{cout ch ;
}
cout endl;
auto自动推导类型范围for可以自动赋值、自动迭代、自动判断结束非常nb但其实也正常因为和之前的第一种方法在底层是一样的。也就是说
范围for的底层就是迭代器。
同时我们要注意如果我们再第一种方法中对*it进行修改那么会直接影响到字符串而我们再范围for中修改是修改不到字符串的
void test_string1()
{string s2(hello world);string::iterator it s2.begin();while (it ! s2.end()){*it 2;cout *it ;it;}cout endl;//s2被修改for (auto ch : s2){ch - 2;cout ch ;}cout endl;//s2没有被修改
}
那这是为什么呢其实是因为我们将*it一个个赋值给ch而我们是对ch进行操作。ch只是一个局部临时变量对其的修改并不会影响到原来的字符串。
那我们可以这么处理
for (auto ch : s2)
{ch - 2;cout ch ;
}
cout endl;
我们只要修改为引用自然就不会有临时变量也就可以直接对原字符串进行操作了。
总结上面三种遍历方式下标[ ]、迭代器、范围for在性能上并没有什么区别第一种和第三种用得可能相对多。
补充auto和范围for
auto关键字
1. 在早期C/C中auto的含义是使用auto修饰的变量是具有自动存储期的局部变量后来这个不重要了。C11中标准委员会变废为宝赋予了auto全新的含义即auto不再是一个存储类型指示符而是作为一个新的类型指示符来指示编译器auto声明的变量必须由编译器在编译时期推导而得。
2. 用auto声明指针类型时用auto和auto*没有任何区别但用auto声明引用类型时则必须加
3. 当在同一行声明多个变量时这些变量必须是相同的类型否则编译器将会报错因为编译器实际只对第一个类型进行推导然后用推导出来的类型定义其他变量。
4. auto不能作为函数的参数可以做返回值但是建议谨慎使用
5. auto不能直接用来声明数组
void test_string2()
{mapstring, string dict;//mapstring, string::iterator mid dict.begin();auto mid dict.begin();
}
看上面的代码由于auto可以自动推导类型我们可以减少一些代码量。
#include iostream
using namespace std;int func1()
{return 10;
}void func()
{int a 10;auto b a;auto d func1();auto c a;auto e;//编译报错:rror C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项cout typeid(b).name() endl;cout typeid(c).name() endl;cout typeid(d).name() endl;
}int main()
{func();return 0;
}
我们可以用【typeid(variate).name()】来打印变量的类型。
#include iostream
using namespace std;int func1()
{return 10;
}auto func2()
{return func1();
}auto func3()
{return func2();
}int main()
{auto ret func3();return 0;
}
auto可以做返回值但是建议谨慎使用如上面代码就有种递归的感觉func3要去查看func2的返回值func2又要去查看func1的返回值。
范围for
1. 对于一个有范围的集合而言由程序员来说明循环的范围是多余的有时候还会容易犯错误。因此C11中引入了基于范围的for循环。for循环后的括号由冒号“ ”分为两部分第一部分是范围内用于迭代的变量第二部分则表示被迭代的范围自动迭代自动取数据自动判断结束。
2. 范围for可以作用到数组和容器对象上进行遍历
3. 范围for的底层很简单容器遍历实际就是替换为迭代器这个从汇编层也可以看到。
范围for适用于容器和数组
#include iostream
#include string
#include list
#include map
using namespace std;int main()
{int array[] { 1,2,3,4,5 };//C98的遍历for (int i 0; i sizeof(array) / sizeof(array[0]); i){array[i] * 2;}for (int i 0; i sizeof(array) / sizeof(array[0]); i){cout array[i] endl;}//C11的遍历for (auto e : array){e * 2;}for (auto e : array){cout e endl;}return 0;
}
注意用范围for遍历数组yyds。 迭代器⭐
C中的迭代器Iterator是一种允许你访问容器中元素的对象而无需暴露容器的内部结构。迭代器提供了一种统一的方法来遍历容器中的所有元素无论容器的具体类型如何如数组、向量vector列表list等。通过使用迭代器你可以读取、写入或删除容器中的元素而无需关心容器的具体实现细节。 我们先简单了解前四个迭代器
接口名称使用说明begin()返回指向第一个元素的迭代器end()返回指向最后一个元素的下一个位置的迭代器rbegin()返回指向最后一个元素的反向迭代器rend()返回指向第一个元素的前一个位置的反向迭代器 对于前两个我们之前就介绍过 而后面两个就是与前两个相反。rbegin指向最后一个元素rbegin那么这个迭代器往回走rend指向的是第一个元素rend那么这个迭代器往后走。 但是我们需要注意上面的四个迭代器都是可读可写的如果这个字符串是const的C11还可以用下面的四个。const迭代器是只读不可写的也就是*cit不能被修改。
//const正向迭代器
void test_string5()
{const string s3(hello world);for (auto cit s3.begin(); cit s3.end(); cit){cout *cit ;}cout endl;
}//const反向迭代器
void test_string6()
{const string s4(hello world);for (auto cit s4.crbegin(); cit s4.crend(); cit){cout *cit ;}cout endl;
}
所以说一般情况下有四种迭代器iterator、const_iterator、reverse_iterator、const_reverse_iterator。
•3.string类对象的容量操作
在C中string类是用来处理字符串的一个非常强大的类。关于string类对象的容量操作主要包括以下几个方面 或如下 函数名称 功能说明 size重点 返回字符串有效字符长度length返回字符串有效字符长度capacity返回空间总大小empty 重点检测字符串释放为空串是返回true否则返回falseclear 重点 清空有效字符 reserve 重点 为字符串预留空间 * * resize 重点将有效字符的个数该成n个多出的空间用字符c填充 size、length、capacity
我们首先来看看【size】和【length】他们都可以计算字符串的长度但是【size】会比【length】更好些因为如果数据结构是一棵二叉树呢所以说后者没有前者通用。 而【capacity】计算的是字符串的空间总大小 void test_string7()
{string s5(hello world);cout s5.length() endl;//11cout s5.size() endl;//11cout s5.capacity() endl;//15cout s5.max_size() endl;//2147483647
}
这里我们也可以简单了解一下【max_size】它返回的是字符串可以达到的最大长度。通过代码我们可以发现它直接返回了int的最大值INT_MAX。
【capacity】并不一定等于字符串的长度它的容量变化我们可以通过下面的代码进行观察
void TestPushBack()
{string s;size_t sz s.capacity();cout capacity changed: sz endl;cout making s grow:\n;for (int i 0; i 100; i){s.push_back(c);if (sz ! s.capacity()){sz s.capacity();cout capacity changed: sz endl;}}
}
运行后我们可以发现大致情况下第一次是二倍扩容后面是1.5倍扩。当然在不同的环境下是不一样的。
reserve⭐
我们现在知道了字符串的扩容那有没有什么办法能减少扩容的次数呢答案是有的reserve就可以解决这个问题。
reserve用于请求改变字符串的容量即分配的内存量以确保字符串可以存储至少指定数量的字符而不需要重新分配内存。这可以提高性能特别是在你知道将要向字符串中添加大量字符时因为重新分配内存特别是当字符串变得很大时可能会非常耗时。 void TestPushBack()
{string s;//预留100的空间s.reserve(100);size_t sz s.capacity();cout capacity changed: sz endl;cout making s grow:\n;for (int i 0; i 100; i){s.push_back(c);if (sz ! s.capacity()){sz s.capacity();cout capacity changed: sz endl;}}
}
但我们可以看到VS上实际是会比100多一些它会自动去对齐。 当然不同平台的实现会有不同。同时注意【reserve】大多不会造成缩容上面文档中也有说明。
void test_string8()
{string s(hello world and merry christmas!);cout s.size() endl;cout s.capacity() endl endl;;//n s.size() 不会缩容s.reserve(20);cout s.size() endl;cout s.capacity() endl endl;//s.size() n s.capacity() 不会缩容s.reserve(35);cout s.size() endl;cout s.capacity() endl endl;//n s.capacity() 增容s.reserve(60);cout s.size() endl;cout s.capacity() endl endl;
}
举例说明VS上是不会造成缩容的。
clear
用于移除字符串中的所有字符将其长度设置为0但保留其当前分配的容量capacity。调用clear后字符串将不再包含任何字符但它在内存中的空间即字符数组的大小不会减小。 void test_string9()
{string s(hello world);cout s endl;//hello worldcout s.size() endl;//11cout s.capacity() endl endl;//15s.clear();cout s endl;//空cout s.size() endl;//0cout s.capacity() endl endl;//15
}
empty
用于检查字符串是否为空。如果字符串不包含任何字符即其长度为0则【empty】函数返回true否则返回false。
【empty】函数是检查字符串是否为空的一种高效且直观的方式因为它直接查询字符串的长度而不需要遍历整个字符串。 void test_string10()
{string s1(hello world);cout s1.empty() endl;//0string s2;cout s2.empty() endl;//1
}
resize
用于改变字符串的大小。它可以增加或减少字符串中的字符数如果增加大小则新添加的字符默认初始化为空字符。在实践中当增加大小时resize通常会用指定的字符如果有的话填充新添加的空间或者如果不指定则不进行填充。
更常见的是resize用于减少字符串的大小此时会移除超出新大小的尾部字符。 resize函数有两种常见的重载形式
void resize(size_t n);
这个版本将字符串的大小更改为n。如果n小于当前大小则移除超出新大小的尾部字符。如果n大于当前大小则添加足够数量的字符以达到新的大小。
void reserve(size_t n, char c);
这个版本还允许你指定一个字符c用于填充在增加大小时新添加的空间。如果n小于当前大小则仍然移除超出新大小的尾部字符。如果n大于当前大小则添加n-size()个字符c。
•4.string类对象的修改操作
string也是一种数据结构那么它就具有增删查改的各种操作。 我们一个一个来看。 push_back、append
【push_back】的功能是在字符串末尾添加一个字符。 而【append】的功能是在字符串的末尾添加字符串它有很多的版本但用法上我们其实都很熟悉了。 #include iostream
using namespace std;void test_string()
{string s(hello world);s.push_back(s);s.push_back(t);s.append(Merry Christmas! );cout s endl;
}int main()
{test_string();return 0;
} 结果如下
operator
【operator】用于将一个字符串追加到另一个string对象的末尾。 void test_string2()
{string s1(hello world);string s2(Happy New Year!);s1 ;s1 Merry Christmas!;s1 ;s1 s2;cout s1 endl;//hello world Merry Christmas! Happy New Year!
}
assgin 【assign】也是一种赋值但是用得比较少。 void test_string3()
{string str;string base The quick brown fox jumps over a lazy dog.;str.assign(base);cout str \n;str.assign(base, 10, 9);cout str \n; // brown foxstr.assign(pangrams are cool, 7);cout str \n; // pangramstr.assign(c-string);cout str \n; // c-stringstr.assign(10, *);cout str \n; // **********str.assign(base.begin() 16, base.end() - 12);std::cout str \n; // fox jumps over
}
insert⭐
【insert】函数是一个非常有用的成员函数它允许你在字符串的指定位置插入另一个字符串或字符。这个函数有几个重载版本以适应不同的插入需求。 void test_string4()
{string s1(hello world);s1.insert(0, Merry Christmas! );//插入一个字符的写法char ch t;s1.insert(0, 1, ch);cout s1 endl;//tMerry Christmas! hello worlds1.insert(s1.begin(), ch);cout s1 endl;//ttMerry Christmas! hello world
}但是注意头插在顺序表中是需要将后面的所有元素都向后移动的复杂度为O(n)。
erase
【erase】用于从字符串中删除一个或多个字符。这个函数有几个重载版本允许你指定要删除的字符的起始位置和长度或者通过迭代器来指定要删除的字符范围。 void test_string6()
{string s(hello world);s.erase(6, 1);cout s endl;//hello orlds.erase(0, 1);cout s endl;//ello orlds.erase(s.begin());//llo orldcout s endl;s.erase(--s.end());//llo orlcout s endl;s.erase(s.size() - 1, 1);cout s endl;//llo ors.erase(2);cout s endl;//ll
}
replace
【replace】用于替换字符串中的一部分内容。这个函数有多个重载版本允许你指定要替换的字符范围以及替换成的新内容。 void test_string7()
{string s1(hello world);string s2(s1);//少替换多-向后移动s1.replace(5, 1, %%);cout s1 endl;//hello%%world//多替换少-向前移动s2.replace(5, 4, %%);cout s2 endl;//hello%%ld
}
swap
【swap】用于交换两个string对象的内部数据。在调用【swap】后两个字符串对象将包含对方原本持有的数据。这种操作通常比手动复制和删除数据要快得多因为它只需要交换内部指针或引用而不需要实际复制字符串内容。 void test_string9()
{string str1 Hello;string str2 World;str1.swap(str2);std::cout str1: str1 std::endl; // 输出: str1: World std::cout str2: str2 std::endl; // 输出: str2: Hello
}
pop_back 【pop_back】用于移除字符串中的最后一个字符。这个函数没有返回值并且它会修改调用它的string对象使其长度减少一个字符。 void test_string10()
{string str Hello, World!;//显示原始字符串 cout Original string: str std::endl;//移除最后一个字符 str.pop_back();//显示修改后的字符串 cout After pop_back: str std::endl;
}
•5.string类对象的其他操作
string类除对象的访问遍历操作、对象的修改操作还有一些其他的字符串操作 下面我就注意给大家介绍。
c_str
该函数返回一个指向以NULL结尾的字符数组C 风格字符串的指针该数组包含了string对象中存储的相同字符序列。这个返回的指针可以用于需要C风格字符串的C或C的API中。 这个接口是连接C字符串与Cstring类对象的桥梁很好地起到了一个连通的效果。
void test_string11()
{string file;cin file;FILE* pf fopen(file.c_str(), r);char ch fgetc(pf);while (ch ! EOF){cout ch;ch fgetc(pf);}fclose(pf);
}
find
【find】用于在字符串中搜索子字符串或字符的首次出现位置。如果找到了指定的子字符串或字符【find】函数会返回子字符串或字符首次出现的索引基于0的索引如果没有找到则返回npos这是一个特殊的常量表示未找到的位置通常是一个很大的整数值。 例题将字符串中的空格替换成%%
void test_string8()
{string s(hello world and Merry Christmas!);size_t pos s.find( );while (pos ! string::npos){s.replace(pos, 1, %%);pos s.find( );}cout s endl;
}
但是当空格较多的时候频繁的扩容、插入、移动操作使得这个过程比较复杂我们还可以用范围for这么来处理
void test_string8()
{string s(hello world and Merry Christmas!);string temp;for (auto ch : s){if (ch )temp %%;elsetemp ch;}cout temp endl;
}
rfind 【rfind】的功能和【find】类似只不过它是倒着找的。如果找到了指定的子串或字符则返回它第一次出现的位置从0开始计数如果没有找到则返回npos这是一个常量表示一个不可能的位置通常是一个非常大的数。 void test_string12()
{string str(The sixth sick sheiks sixth sheeps sick.);string key(sixth);size_t found str.rfind(key);if (found ! string::npos)str.replace(found, key.length(), seventh);//The sixth sick sheiks seventh sheeps sick.cout str \n;
}
substr⭐
【substr】是string 类中的一个成员函数它允许你从一个字符串中提取或称为“截取”一个子字符串。这个函数非常有用特别是在需要处理字符串的一部分时。 举例1打印test.cpp的后缀.cpp
void test_string13()
{string s1(test.cpp);size_t pos s1.find(.);string suffix s1.substr(pos);cout suffix endl;
} 举例2打印test.cpp.zip的后缀.zip
void test_string13()
{string s1(test.cpp.zip);size_t pos s1.rfind(.);string suffix s1.substr(pos);cout suffix endl;
}
find_first_of
它用于在字符串中查找第一次出现与指定字符集合中任意字符相匹配的字符的位置。这个函数在你需要查找分隔符、查找属于某个字符集的第一个字符时特别有用。 举例将str中的aeiou全部屏蔽为 *
void test_string14()
{string str(Please, replace the vowels in this sentence by asterisks.);size_t pos str.find_first_of(aeiou);while (pos ! string::npos){str[pos] *;pos str.find_first_of(aeiou, pos 1);}cout str \n;
}
find_last_of
同样的道理【find_last_of】和【find_first_of】类似只不过是从末尾开始找的。 举例分割文件路径
void SplitFilename(const string str)
{cout Splitting: str \n;size_t found str.find_last_of(/\\);cout path: str.substr(0, found) \n;cout file: str.substr(found 1) \n;
}int main()
{string str1(/usr/bin/man);string str2(c:\\windows\\winhelp.exe);SplitFilename(str1);SplitFilename(str2);return 0;
}
find_first_not_of 这个就和【find_first_not_of】相反了查找不是指定字符集合中的字符并返回它的位置是从头开始查找如果找不到就返回string::npos。 举例将str中的除aeiou的字符全部屏蔽为 *
void test_string14()
{string str(Please, replace the vowels in this sentence by asterisks.);size_t pos str.find_first_not_of(aeiou);while (pos ! string::npos){str[pos] *;pos str.find_first_not_of(aeiou, pos 1);}cout str \n;
}
find_last_not_of 同理这个和【find_last_not_of】相反查找不是指定字符集合中的字符并返回它的位置是从尾开始查找如果找不到就返回string::npos。 举例删除空白字符
void test_string15()
{string str(Please, erase trailing white-spaces \n);string whitespaces( \n);size_t found str.find_last_not_of(whitespaces);if (found ! string::npos)str.erase(found 1);elsestr.clear();std::cout [ str ]\n;
}
•6.string类的非成员函数
string类提供了丰富的成员函数来操作字符串但也有一些与string相关的非成员函数这些函数虽然不是string类的一部分但经常与string 一起使用以提供额外的功能或操作。
这些非成员函数通常定义在string或其他相关的头文件中并且它们通常是为了支持更通用的操作或算法这些操作可能不特定于string但可以与string 一起很好地工作。 operator
【operator】用于将两个string对象、或者一个string对象和一个C风格字符串const char*连接起来生成一个新的string 对象该对象包含了两个操作数连接后的结果。 void test_string16()
{string s1(hello);string s2 s1 world;cout s2 endl;string s3 world s1;cout s3 endl;
}
relational operators
string类提供了关系运算符relational operators来比较两个字符串对象。这些关系运算符不是string类的成员函数而是作为非成员函数重载的以便它们能够接收两个string类型的操作数。这些运算符返回布尔值true或false表示字符串之间的比较结果功能上类似于C语言的【strcmp】函数。 以下是string类支持的关系运算符
等于检查两个字符串是否包含相同的字符序列。
不等于!检查两个字符串是否不同。
小于按字典顺序比较两个字符串。如果第一个字符串在字典上小于第二个字符串则返回 true。
大于按字典顺序比较两个字符串。如果第一个字符串在字典上大于第二个字符串则返回 true。
小于等于按字典顺序比较两个字符串。如果第一个字符串在字典上小于或等于第二个字符串则返回 true。
大于等于按字典顺序比较两个字符串。如果第一个字符串在字典上大于或等于第二个字符串则返回 true。
void test_string17()
{string foo alpha;string bar beta;if (foo bar) std::cout foo and bar are equal\n;if (foo ! bar) std::cout foo and bar are not equal\n;if (foo bar) std::cout foo is less than bar\n;if (foo bar) std::cout foo is greater than bar\n;if (foo bar) std::cout foo is less than or equal to bar\n;if (foo bar) std::cout foo is greater than or equal to bar\n;
}
结果如下:
foo and bar are not equal
foo is less than bar
foo is less than or equal to bar
getline
【getilne】是一个非成员函数它可以从输入流中读取字符直到遇到换行符\n然后丢弃换行符并将读取的字符序列不包括换行符存储到提供的string对象中。 举例字符串最后一个单词的长度
#include iostream
using namespace std;int main()
{string str;//cin和scanf拿不到空格//cin str;getline(cin, str);size_t pos str.rfind( );cout str.size() - pos - 1 endl;return 0;
} •7.例题训练
那么学习到现在我们已经具备了写一些简单题目的能力那就来几道题目练练手吧
题目一仅仅反转字母
题目链接917. 仅仅反转字母 - 力扣LeetCode 思路设置左右边界向中间走如果不是字母就继续往中间走知道都是字母就交换整个思路和快速排序的霍尔方法差不多。
代码如下
class Solution {
public:string reverseOnlyLetters(string s) {int left 0;int right s.size() - 1;while(left right){while(!isalpha(s[right]) left right){right--;}while(!isalpha(s[left]) left right){left;}swap(s[left], s[right--]);}return s;}
};
大家不要学习了我们string类的相关操作就忘记了普通而本质的方法如同不要像学习数学只顾秒杀而忽略了最基本的方法。
题目二第一个唯一字符
题目链接387. 字符串中的第一个唯一字符 - 力扣LeetCode
题目描述 思路直接利用计数排序的思想统计所有字母字符出现的次数再寻找第一个为1的数组元素就可以了。
代码如下
class Solution {
public:int firstUniqChar(string s) {int count[26] { 0 };//统计次数for(auto ch : s){count[ch - a];}for(size_t i 0; i s.size(); i){if(count[s[i] - a] 1)return i;}return -1;}
};
题目三字符串相加
题目链接415. 字符串相加 - 力扣LeetCode
题目描述 思路 实现两个字符串表示的非负整数相加时我们主要利用字符串从右到左即最低位到最高位的顺序模拟手工加法的过程。
class Solution {
public:string addStrings(string num1, string num2) {string str;int end1 num1.size() - 1, end2 num2.size() - 1;//进位int next 0;while(end1 0 || end2 0){int val1 end1 0 ? num1[end1--] - 0 : 0;int val2 end2 0 ? num2[end2--] - 0 : 0;int ret val1 val2 next;next ret / 10;ret % 10;//头插str.insert(str.begin(), 0 ret);}if(next ! 0){//头插str.insert(str.begin(), 1);}return str;}
};
关键点
从低位到高位相加模拟手工加法的过程从字符串的末尾即最低位开始相加。处理不同长度的字符串通过检查索引是否越界将不存在的位视为0从而统一处理不同长度的字符串。进位处理使用变量next来记录进位值并在每次相加时更新。结果字符串的构建使用头插法构建结果字符串确保最终得到的字符串顺序是正确的。
但是这个方法其实不太好原因就是不断头插构成O(n^2)的时间复杂度因此我们可以用尾插的方法
class Solution {
public:string addStrings(string num1, string num2) {string str;int end1 num1.size() - 1, end2 num2.size() - 1;//进位int next 0;while(end1 0 || end2 0){int val1 end1 0 ? num1[end1--] - 0 : 0;int val2 end2 0 ? num2[end2--] - 0 : 0;int ret val1 val2 next;next ret / 10;ret % 10;//尾插str (0 ret);}if(next ! 0){str 1;}//逆置reverse(str.begin(), str.end());return str;}
};
此时是时间复杂度就降低至O(n)。 • ✨SumUp结语 到这里本篇文章的内容就结束了本节介绍了C中string类的相关知识。这里的内容非常多非常丰富下一篇章还需要继续讲解。望大家能够认真学习打好基础迎接接下来的挑战期待大家继续捧场~