网站如何做好用户体验,百度知道问答首页,个人主页经典句子,个人做网站多少钱string 类的重要性及与 C 语言字符串对比 在 C 语言中#xff0c;字符串是以 \0 结尾的字符集合#xff0c;操作字符串需借助 C 标准库的 str 系列函数#xff0c;但这些函数与字符串分离#xff0c;不符合 OOP 思想#xff0c;且底层空间管理易出错。而在 C 中#xff0… string 类的重要性及与 C 语言字符串对比 在 C 语言中字符串是以 \0 结尾的字符集合操作字符串需借助 C 标准库的 str 系列函数但这些函数与字符串分离不符合 OOP 思想且底层空间管理易出错。而在 C 中string 类很好地解决了这些问题在面试、日常工作等场景中处理字符串相关任务时string 类的使用更为普遍。 补充知识点 auto 关键字类型推导的利器 1.指针与引用类型声明 当我们用 auto 声明指针类型时使用 auto 和 auto * 是没有任何区别的。比如 int x 10;
auto y x;
auto* z x; 在上述代码中y 和 z 的类型推导结果是一样的都是指向 int 类型的指针。 但是当用 auto 声明引用类型时就必须加上 。例如 int x 10;
auto m x; 2.同一行多变量声明 需要特别注意的是当在同一行声明多个变量时这些变量必须是相同的类型。因为编译器实际上只对第一个变量的类型进行推导然后用推导出来的类型去定义其他变量。 如果类型不一致就会导致编译报错。比如下面的代码 auto aa 1, bb 2; // 正确都是int类型
// 编译报错error C3538: 在声明符列表中“auto”必须始终推导为同一类型
auto cc 3, dd 4.0; 3.函数参数与返回值 auto 不能作为函数的参数哦像下面这样的写法是不行的 // 不能做参数
void func2(auto a)
{} 不过auto 是可以作为函数的返回值的但建议谨慎使用。例如C20版本之后才能用 // 可以做返回值但是建议谨慎使用
auto func3()
{return 3;
} 4.数组声明 auto 也不能直接用来声明数组如下所示的代码会报错 // 编译报错error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型
auto array[] { 4, 5, 6 }; 5.总结 便利性 范围 for 循环简洁的遍历方式 传统遍历方式的痛点 #includeiostreamint main() {int array[] { 1, 2, 3, 4, 5 };// C98的遍历方式for (int i 0; i sizeof(array) / sizeof(array[0]); i) {// 这里可以进行对数组元素的操作比如输出std::cout array[i] ;}std::cout std::endl;return 0;
} 范围 for 循环的基本语法形式 for (元素类型 元素变量 : 可迭代对象) {// 在这里对元素变量进行操作比如输出、修改等
} 范围 for 循环的便捷之处 1.自动处理迭代细节 #includeiostream
#include stringint main() {std::string str(hello world);// 使用范围for循环遍历字符串for (auto ch : str) {std::cout ch ;}std::cout std::endl;return 0;
} 2.适用于多种数据结构 #includeiostream
#include string
#include mapint main() {std::mapstd::string, std::string dict { { apple, 苹果 },{ orange, 橙子 }, {pear,梨} };// 使用范围for循环遍历mapfor (auto it : dict) {std::cout it.first : it.second std::endl;}return 0;
} 3. 简洁的代码风格 传统方式 #includeiostream
#include vectorint main() {std::vectorint vec { 1, 2, 3, 4, 5 };// 传统的遍历方式使用迭代器std::vectorint::iterator it;for (it vec.begin(); it! vec.end(); it) {std::cout *it ;}std::cout std::endl;return 0;
} 范围 for 循环方式 #includeiostream
#include vectorint main() {std::vectorint vec { 1, 2, 3, 4, 5 };// 使用范围for循环遍历for (auto num : vec) {std::cout num ;}std::cout std::endl;return 0;
} 迭代器容器遍历好帮手 概念与作用 迭代器类似指针能统一访问容器元素无需关心容器具体实现方便遍历各种容器。 分类 正向迭代器按元素存储顺序从首到尾遍历如std::string遍历示例。 #include iostream
#include stringint main() {std::string str Hello World;// 使用正向迭代器遍历stringstd::string::iterator it;for (it str.begin(); it! str.end(); it) {std::cout *it;}std::cout std::endl;return 0;
} 反向迭代器从容器尾到头遍历像std::string的反向遍历代码。 #include iostream
#include stringint main() {std::string str Hello World;// 使用反向迭代器遍历stringstd::string::reverse_iterator rit;for (rit str.rbegin(); rit! str.rend(); rit) {std::cout *rit;}std::cout std::endl;return 0;
} 常量迭代器用于不修改元素的遍历如常量std::string的情况。 #include iostream
#include stringint main() {const std::string str Hello World;// 使用常量迭代器遍历常量stringstd::string::const_iterator cit;for (cit str.begin(); cit! str.end(); cit) {std::cout *cit;}std::cout std::endl;return 0;
} 因为str被声明为常量所以必须用常量迭代器cit来遍历若用普通迭代器尝试修改字符会报错。 常用操作 解引用*获取迭代器指向元素的值。 std::cout *it ; 这里的*it就是获取迭代器it所指向的整数元素的值并将其输出。 递增递减、--移动迭代器位置。 for (it str.begin(); it! str.end(); it) 比较、!判断迭代器位置关系。 for (it string.begin(); it! string.end(); it) {//...
} string 类的基础特性 string类对象的常见构造 string 类实现了多个构造函数重载常用构造函数如下 string(); //构造一个空字符串string(const char* s); //复制s所指的字符序列string(const char* s, size_t n); //复制s所指字符序列的前n个字符string(size_t n, char c); //生成n个c字符的字符串string(const string str); //生成str的复制品string(const string str, size_t pos, size_t len npos); //复制str中从字符位置pos开始并跨越len个字符的部分使用方法 string s1; //构造空字符串
string s2(hello string); //复制hello string
string s3(hello string, 3); //复制hello string的前3个字符
string s4(10, s); //生成10个s字符的字符串
string s5(s2); //生成s2的复制品
string s6(s2, 0, 4); //复制s2中从字符位置0开始并跨越4个字符的部分 string 类大小和容量相关操作 获取有效字符个数size() length() 使用size()或length()函数二者底层实现原理相同一般常用size() string s(CSDN);
cout s.size() endl; //4
cout s.length() endl; //4 获取最大可包含字符数max_size() 通过max_size()函数获取。 string s(CSDN);
cout s.max_size() endl; //4294967294 获取分配的存储空间大小capacity() capacity()函数可获取当前对象所分配的存储空间大小。 string s(CSDN);
cout s.capacity() endl; //15 改变有效字符个数resize() resize()函数可改变当前对象的有效字符个数遵循一定规则如 n 大于当前 size 时扩大小于时缩小。 string s(CSDN);
s.reserve(20);
cout s endl; //CDSN
cout s.size() endl; //4
cout s.capacity() endl; //31 改变容量大小reserve() reserve()函数用于改变当前对象的容量大小当 n 大于当前 capacity 时扩大小于时不做处理。 string s(CSDN);
s.reserve(20);
cout s endl; //CDSN
cout s.size() endl; //4
cout s.capacity() endl; //31 删除对象内容clear() clear()函数可删除对象内容使其变为空字符串。 string s(CSDN);
s.clear();
cout s endl; //空字符串 判断对象是否为空empty() empty()函数用于判断对象是否为空返回布尔值。 string s(CSDN);
cout s.empty() endl; //0
s.clear();
cout s.empty() endl; //1 string 类的常见操作 string类对象的修改操作 插入操作 1.尾插push_back() 使用push_back()函数可在字符串末尾插入单个字符。 #include iostream
#include string
using namespace std;
int main()
{string s;s.push_back(C);s.push_back(S);s.push_back(D);s.push_back(N);cout s endl; //CSDNreturn 0;
} 2.指定位置插入insert() insert()函数有多种重载形式可在指定位置插入字符串、字符等。 #include iostream
#include string
using namespace std;
int main()
{string s(C); //C//insert(pos, str)在pos位置插入字符串strs.insert(1, S); //CS//insert(pos, string)在pos位置插入string对象string t(D);s.insert(2, t); //CSD//insert(pos, char)在pos位置插入字符chars.insert(s.end(), N); //CSDNcout s endl; //CSDNreturn 0;
} 3.拼接操作append() 使用append()函数可完成 string 的拼接有多种重载形式可拼接 string 对象、字符串、多个相同字符等。 #include iostream
#include string
using namespace std;
int main()
{string s1(I);string s2( like);//append(string)完成两个string对象的拼接s1.append(s2); //I like//append(str)完成string对象和字符串str的拼接s1.append( C); //I like C//append(n, char)将n个字符char拼接到string对象后面s1.append(3, !); //I like C!!!cout s1 endl; //I like C!!!return 0;
} 4.operator 其实上面的append和push_back我们除了特定的方法我们是不会使用到的 我们使用就行了 int main()
{string s3(hello);s3 ,;s3 world;cout s3 endl;//helloworldreturn 0;
}删除操作 1.尾删pop_back() pop_back()函数可删除字符串末尾的字符。 #include iostream
#include string
using namespace std;
int main()
{string s(C);s.pop_back();s.pop_back();cout s endl; //Creturn 0;
} 2.指定位置删除erase() erase()函数有多种重载形式可删除指定位置、指定范围的字符。 #include iostream
#include string
using namespace std;
int main()
{string s(I like C!!!);//erase(pos, n)删除pos位置开始的n个字符s.erase(8, 5); //I like C//erase(pos)删除pos位置的字符s.erase(s.end()-1); //I like//erase(pos1, pos2)删除[pos1, pos2)上所有字符s.erase(s.begin() 1, s.end()); //Icout s endl; //Ireturn 0;
} 查找操作 1.正向查找find() find()函数可正向搜索第一个匹配项可查找 string 对象、字符串、字符等。 #include iostream
#include string
using namespace std;
int main()
{string s1(http://www.cplusplus.com/reference/string/string/find/);//find(string)正向搜索与string对象所匹配的第一个位置string s2(www);size_t pos1 s1.find(s2);cout pos1 endl; //7//find(str)正向搜索与字符串str所匹配的第一个位置char str[] cplusplus.com;size_t pos2 s1.find(str);cout pos2 endl; //11//find(char)正向搜索与字符char所匹配的第一个位置size_t pos3 s1.find(:);cout pos3 endl; //4return 0;
} 2.反向查找rfind() rfind()函数可反向搜索第一个匹配项同样可查找多种类型。 #include iostream
#include string
using namespace std;
int main()
{string s1(http://www.cplusplus.com/reference/string/string/find/);//rfind(string)反向搜索与string对象所匹配的第一个位置string s2(string);size_t pos1 s1.rfind(s2);cout pos1 endl; //42//rfind(str)反向搜索与字符串str所匹配的第一个位置char str[] reference;size_t pos2 s1.rfind(str);cout pos2 endl; //25//rfind(char)反向搜索与字符char所匹配的第一个位置size_t pos3 s1.rfind(/);cout pos3 endl; //53return 0;
} 比较操作 compare() 使用compare()函数完成比较有多种重载形式根据比较规则返回不同结果。 #include iostream
#include string
using namespace std;
int main()
{string s1(hello world);string s2(hello CSDN);//hello world和hello CSDN比较cout s1.compare(s2) endl; //1//ell和hello CSDN比较cout s1.compare(1, 3, s2) endl; //-1//hello和hello比较cout s1.compare(0, 4, s2, 0, 4) endl; //0return 0;
} 替换操作 replace() 使用replace()函数完成 string 的替换有多种重载形式可将指定位置的字符或字符序列替换为其他内容。 #include iostream
#include string
using namespace std;
int main()
{string s(hello world);//replace(pos, len, str)将pos位置开始的len个字符替换为字符串strs.replace(6, 4, CSDN); //hello CSDNd//replace(pos, len, n, char)将pos位置开始的len个字符替换为n个字符chars.replace(10, 1, 3, !); //hello CSDN!!!cout s endl;return 0;
} 交换操作 swap() 使用swap()函数可完成两个 string 类的交换有成员函数和非成员函数两种形式。 #include iostream
#include string
using namespace std;
int main()
{string s1(hello);string s2(CSDN);//使用string类的成员函数swap交换s1和s2s1.swap(s2);cout s1 endl; //CSDNcout s2 endl; //hello//使用非成员函数swap交换s1和s2swap(s1, s2);cout s1 endl; //hellocout s2 endl; //CSDNreturn 0;
} string 类中元素的访问 string类对象的访问及遍历操作 通过下标访问 operator[] string 类对[ ]运算符进行了重载可直接使用[ ] 下标访问和修改对象中的元素。 #include iostream
#include string
using namespace std;
int main()
{string s(CSDN);//[]下标访问对象元素for (size_t i 0; i s.size(); i){cout s[i];}cout endl;//[]下标修改对象元素内容for (size_t i 0; i s.size(); i){s[i] x;}cout s endl; //xxxxreturn 0;
} 通过 at 函数访问 at at函数也可访问和修改对应位置的元素。 #include iostream
#include string
using namespace std;
int main()
{string s(CSDN);for (size_t i 0; i s.size(); i){//at(pos)访问pos位置的元素cout s.at(i);}cout endl;for (size_t i 0; i s.size(); i){//at(pos)访问pos位置的元素并对其进行修改s.at(i) x;}cout s endl; //xxxxreturn 0;
} 通过范围 for 访问 for 若要通过范围 for 修改对象元素接收元素的变量 e 的类型必须是引用类型。 #include iostream
#include string
using namespace std;
int main()
{string s(CSDN);//使用范围for访问对象元素for (auto e : s){cout e;}cout endl; //CSDN//使用范围for访问对象元素并对其进行修改for (auto e : s) //需要修改对象的元素e必须是引用类型{e x;}cout s endl; //xxxxreturn 0;
} 通过迭代器访问 可使用迭代器访问和修改对象中的元素。 #include iostream
#include string
using namespace std;
int main()
{string s(CSDN);//使用迭代器访问对象元素string::iterator it1 s.begin();while (it1! s.end()){cout *it1;it1;}cout endl; //CSDN//使用迭代器访问对象元素并对其进行修改string::iterator it2 s.begin();while (it2! s.end()){*it2 1;it2;}cout s endl; //DTEOreturn 0;
} string 类中运算符的使用 string类非成员函数 赋值运算符 string 类对运算符进行了重载支持 string 类、字符串、字符的赋值。 #include iostream
#include string
using namespace std;
int main()
{string s1;string s2(CSDN);//支持string类的赋值s1 s2;cout s1 endl; //CSDN//支持字符串的赋值s1 hello;cout s1 endl; //hello//支持字符的赋值s1 x;cout s1 endl; //xreturn 0;
} 复合赋值运算符 对运算符进行了重载支持多种类型的复合赋值。 #include iostream
#include string
using namespace std;
int main()
{string s1;string s2(hello);//支持string类的复合赋值s1 s2;cout s1 endl; //hello//支持字符串的复合赋值s1 CSDN;cout s1 endl; //hello CSDN//支持字符的复合赋值s1 !;cout s1 endl; //hello CSDN!return 0;
} 加法运算符 重载后的运算符支持多种类型的相加操作相加后均返回一个 string 类对象。 #include iostream
#include string
using namespace std;
int main()
{string s;string s1(super);string s2(man);char str[] woman;char ch !;//string类 string类s s1 s2;cout s endl; //superman//string类 字符串s s1 str;cout s endl; //superwoman//字符串 string类s str s1;cout s endl; //womansuper//string类 字符s s1 ch;cout s endl; //super!//字符 string类s ch s1;cout s endl; //!superreturn 0;
} 输入输出运算符 和 对和运算符进行了重载可直接用于 string 类的输入和输出。 #include iostream
#include string
using namespace std;
int main()
{string s;cin s; //输入cout s endl; //输出return 0;
} 关系运算符 对一系列关系运算符、!、、、、进行了重载支持多种类型之间的关系比较比较的是对应字符的 ASCII 码值。 牛刀小试 一、仅仅反转字母 题目描述 给定一个字符串要返回 “反转后的” 字符串其中不是字母的字符都保留在原地而所有字母的位置发生反转。比如输入 “a-bC-dEf-ghIj”输出应为 “j-Ih-gfE-dCba”。 思路 这里采用了双指针的方法。开始时头指针指向字符串开头尾指针指向字符串末尾\0 的前一个字符。头指针先向后寻找待反转字母尾指针再向前寻找待反转字母然后反转这两个待反转字母。重复这个步骤直到头指针和尾指针发生错位为止。 代码实现 class Solution {
public://判断字符ch是否是字母bool IsLetter(char ch) {if ((ch a ch z) || (ch A ch Z))return true;elsereturn false;}//仅仅反转字母string reverseOnlyLetters(string s) {if (s.empty()) //若s为空字符串则返回其本身return s;size_t begin 0, end s.size() - 1; //定义头指针和尾指针用于反转字母while (begin end) //当还有字母可反转时循环继续{while (begin end !IsLetter(s[begin])) //头指针寻找待反转字母begin;while (begin end !IsLetter(s[end])) //尾指针寻找待反转字母end--;swap(s[begin], s[end]); //交换这两个待反转字母begin;end--;}return s; //返回反转后的字符串}
}; 二、字符串中的第一个唯一字 题目描述 给定一个字符串要找到它的第一个不重复的字母并返回它的索引。如果不存在则返回 -1。例如对于字符串 “loveleetcode”应返回 2 。 思路 用一个含有 26 个元素的数组来统计 26 个字母在字符串中出现的次数然后再遍历一次字符串寻找字符串中第一个只出现一次的字母并返回它的索引若不存在则返回 -1 。 代码实现 class Solution {
public:int firstUniqChar(string s) {size_t table[26] { 0 };//统计26个字母在字符串中出现的次数for (size_t i 0; i s.size(); i) {table[s[i] - a];}//寻找字符串中的第一个只出现一次字母for (size_t i 0; i s.size(); i) {if (table[s[i] - a] 1)return i; //返回下标索引}return -1; //不存在返回-1}
}; 三、字符串最后一个单词的长度 题目描述 要计算字符串最后一个单词的长度单词以空格隔开字符串长度小于 5000 。比如输入 “hello CSDN”输出应为 4 。 思路 先找到字符串中最后一个空格的位置空格之后的字符个数就是最后一个单词的长度。若字符串中不存在空格则字符串的长度就是最后一个单词的长度。这里的重点是读取含有空格的字符串因为操作符 读取到空格便会停止所以要用到 getline 函数来读取目标字符串。 代码实现 #include iostream
#include string
using namespace std;int main() {string s;getline(cin, s); //从cin读取一行含有空格的字符串size_t pos s.rfind( ); //获取字符串中最后一个空格的位置if (pos string::npos) //字符串中不含空格{//输出字符串的长度cout s.size() endl;}else //字符串中含有空格{//输出字符串的长度 - 最后一次出现空格的位置 - 1cout s.size() - pos - 1 endl;}return 0;
} 四、验证回文串 题目描述 给定一个字符串要验证它是否是回文串只考虑字母和数字字符可以忽略字母的大小写。例如输入 “A man, a plan, a canal: Panama”输出应为 true 。 思路 首先将字符串中所有的大写字母转换为小写字母然后使用双指针方法。头指针指向字符串开头尾指针指向字符串末尾\0 的前一个字符。头指针先向后寻找待判断的字母或数字字符尾指针再向前寻找待判断的字母或数字字符然后判断这两个字符是否相等若相等则继续下一次判断若不相等则该字符串不是回文串。重复这个步骤直到头指针和尾指针发生错位为止此时便可确定该字符串是回文串。 代码实现 class Solution {
public://判断ch是否是小写字母或数字字符bool isLetterOrNumber(char ch) {if ((ch a ch z)|| (ch 0 ch 9))return true;elsereturn false;}//验证回文串bool isPalindrome(string s) {//将字符串中所有大写字母转为小写字母for (auto ch : s) {if (ch A ch Z)ch 32;}int begin 0, end s.size() - 1; //定义头指针和尾指针用于验证回文串while (begin end) //当还有字母未判断时循环继续{while (begin end !isLetterOrNumber(s[begin]) //头指针寻找待判断的字母或数字字符begin;while (begin end !isLetterOrNumber(s[end]) //尾指针寻找待判断的字母或数end--;if (s[begin] s[end]) //待判断字符相等继续下一次判断{begin;end--;}else{return false; //待判断字符不相等该字符串不是回文串}}return true; //字符串中所有字符判断完毕该字符串是回文串}
}; 五、字符串相加 题目描述 给定两个字符串形式的非负整数 num1 和 num2要计算它们的和。比如输入 “999”“1”输出应为 “1000”。 思路 和平时计算两个数的和类似从两个字符串的最后一个数字开始进行相加并设置一个变量记录是否需要进位。每一位置的数字 字符串 1 对应位置的数字 字符串 2 对应位置的数字 进位变量若相加后该位置的数字大于 9则说明需要进位这时设置进位变量为 1并将该位置的数字减去 10 后的结果作为相加后该位置的数字。如此进行下去直到两个字符串都遍历完毕。特别注意两个字符串相加结束后还需要判断进位变量是否为 1若为 1则需要头插一个字符 1 到最终的字符串中。 原始代码实现 class Solution {
public:string addStrings(string num1, string num2) {int end1 num1.size() - 1, end2 num2.size() - 1; //定义两个字符串的尾指针string RetStr; //存储两个字符串相加后的结果int next 0; //标识进位while (end1 0 || end2 0) //两个字符串中有一个未遍历完则继续循环{int val1 0; //第一个字符串等待相加的数字if (end1 0){val1 num1[end1] - 0;end1--;}int val2 0; //第二个字符串等待相加的数字if (end2 0)val2 num2[end2] - 0;end2--;int RetVal val1 val2 next; //两个数字相加后的结果注意需要加上进位if (RetVal 9) //判断是否需要进位{RetVal - 10;next 1; //需要进位设置next为1}else{next 0; //不需进位设置next为0}RetStr.insert(0, 1, RetVal 0); //将RetVal头插到RetStr}if (next 1) //判断是否还需进位RetStr.insert(0, 1, 1); //将1头插到RetStrreturn RetStr; //返回这两个字符串相加后的结果}
}; 优化思路及代码 原始代码中每得到一个位置的结果就头插一个数字到最终的字符串中时间复杂度较高。优化的方法是将得到的每一位数字都尾插到字符串后面只需最后进行一次字符串反转即可。 优化后的代码 class Solution {
public:string addStrings(string num1, string num2) {int end1 num1.size() - 1, end2 num2.size() - 1; //定义两个字符串的尾指针string RetStr; //存储两个字符串相加后的结果int next 0; //标识进位while (end1 0 || end2 0) //两个字符串中有一个未遍历完则继续循环{int val1 0; //第一个字符串等待相加的数字if (end1 0){val1 num1[end1] - 0;end1--;}int val2 0; //第二个字符串等待相加的数字if (end2 0)val2 num2[end2] - 0;end2--;int RetVal val1 val2 next; //两个数字相加后的结果注意需要加上进位if (RetVal 9) //判断是否需要进位{RetVal - 10;next 1; //需要进位设置next为1}else{next 0; //不需进位设置next为0}RetStr (RetVal 0); //将RetVal尾插到RetStr}if (next 1) //判断是否还需进位RetStr 1; //将1尾插插到RetStrreverse(RetStr.begin(), RetStr.end()); //将字符串RetStr进行反转return RetStr; //返回这两个字符串相加后的结果}
};