C++ 之弦上舞:string 类与多样字符串操作的优雅旋律
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 是可以作为函数的返回值的,但建议谨慎使用。例如:(C++20版本之后才能用)
// 可以做返回值,但是建议谨慎使用 auto func3() {return 3; }
4.数组声明
auto 也不能直接用来声明数组,如下所示的代码会报错:
// 编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型 auto array[] = { 4, 5, 6 };
5.总结
便利性
范围 for 循环:简洁的遍历方式
传统遍历方式的痛点
#include<iostream>int main() {int array[] = { 1, 2, 3, 4, 5 };// C++98的遍历方式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.自动处理迭代细节
#include<iostream> #include <string>int main() {std::string str("hello world");// 使用范围for循环遍历字符串for (auto ch : str) {std::cout << ch << " ";}std::cout << std::endl;return 0; }
2.适用于多种数据结构
#include<iostream> #include <string> #include <map>int main() {std::map<std::string, std::string> dict = { { "apple", "苹果" },{ "orange", "橙子" }, {"pear","梨"} };// 使用范围for循环遍历mapfor (auto& it : dict) {std::cout << it.first << ":" << it.second << std::endl;}return 0; }
3. 简洁的代码风格
传统方式:
#include<iostream> #include <vector>int main() {std::vector<int> vec = { 1, 2, 3, 4, 5 };// 传统的遍历方式,使用迭代器std::vector<int>::iterator it;for (it = vec.begin(); it!= vec.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0; }
范围 for 循环方式:
#include<iostream> #include <vector>int main() {std::vector<int> 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 <string>int 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 <string>int 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 <string>int 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;//hello,worldreturn 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; //返回这两个字符串相加后的结果} };
相关文章:
C++ 之弦上舞:string 类与多样字符串操作的优雅旋律
string 类的重要性及与 C 语言字符串对比 在 C 语言中,字符串是以 \0 结尾的字符集合,操作字符串需借助 C 标准库的 str 系列函数,但这些函数与字符串分离,不符合 OOP 思想,且底层空间管理易出错。而在 C 中࿰…...
ElementUI:el-drawer实现在父组件区域内打开抽屉组件非全屏
我们在开发ElementUI的时候遇到抽屉组件全屏的问题,但是我们需要在指定div中展示出来,上代码: 1、在el-drawer中增加属性 el-drawerstyle"position: absolute"z-index"-1":append-to-body"false">// do s…...
HarmonyOS开发中,如何高效定位并分析内存泄露相关问题
HarmonyOS开发中,如何高效定位并分析内存泄露相关问题 (1)Allocation的应用调试方式Memory泳道Native Allocation泳道 (2)Snapshot(3)ASan的应用使用约束配置参数使能ASan方式一方式二 启用ASanASan检测异常码 (4)HWASan的应用功能介绍约束条件使能HWASan方式一方式…...
Unity类银河战士恶魔城学习总结(P156 Audio Settings音频设置)
【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili 教程源地址:https://www.udemy.com/course/2d-rpg-alexdev/ 本章节实现了音频的大小设置与保存加载 音频管理器 UI_VolumeSlider.cs 定义了 UI_VolumeSlider 类,用于处理与音频设置相关的…...
SpringMVC工作原理【流程图+文字详解SpringMVC工作原理】
SpringMVC工作原理 前端控制器:DispactherServlet处理器映射器:HandlerMapping处理器适配器:HandlerAdapter处理器:Handler,视图解析器:ViewResolver视图:View 首先用户通过浏览器发起HTTP请求…...
web五、元素尺寸和位置、节点操作(DOM,查找节点,增加节点,删除节点)、阶段案例
一、元素尺寸与位置 注意:offset家族返回不带单位的数字,而且都是只读的 1,元素尺寸(大小) 大小: offsetWidth和offsetHeight 获取元素的自身宽高、包含元素自身设置的宽高、padding、border 返回的是…...
Vue前端开发-路由树配置
一个配置路由的文件由导入路由模块、创建路由对象和导出路由对象三个部分组成,在创建路由对象时,需要构建路由数组,路由数组中包括一级、二级和多级路由结构,因此,这种结构的路由配置,又称为路由树配置。 …...
记录一次网关异常
记一次网关异常 网关时不时就会出现下面的异常。关键是不知道什么时候就会报错,并且有时候就算什么都不操作,也会导致这个异常。 ERROR org.springframework.scheduling.support.TaskUtils$LoggingErrorHandler - Unexpected error occurred in schedul…...
级联树结构TreeSelect和上级反查
接口返回结构 前端展示格式 前端组件 <template><div ><el-scrollbar height"70vh"><el-tree :data"deptOptions" :props"{ label: label, children: children }" :expand-on-click-node"false":filter-node-me…...
家政小程序开发,打造便捷家政生活小程序
目前,随着社会人就老龄化和生活压力的加重,家政服务市场的需求正在不断上升,家政市场的规模也正在逐渐扩大,发展前景可观。 在市场快速发展的影响下,越来越多的企业开始进入到市场中,同时家政市场布局也发…...
Redis和MySQL之间如何进行数据同步
原因 为什么要进行Redis和MySQL的数据同步? 性能优化:MySQL是关系型数据库,数据读取和存储相对复杂;Redis是内存数据库,读写速度极快,将热点数据存在Redis,可以大大提高系统的访问速度。 数据…...
Kafka的学习路径规划
目录标题 1. 记(记忆力)Kafka核心概念Kafka关键配置 2. 懂(理解力)Kafka工作原理Kafka核心功能Kafka架构设计 3. 网(知识网络)技术栈整合用例和场景 4. 拓(全面拓展)学习材料多样化内…...
SpringBoot 框架下基于 MVC 的高校办公室行政事务管理系统:设计开发全解析
2系统开发环境 2.1vue技术 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式JavaScript框架。 [5] 与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第…...
【JavaEE】Spring Boot 项目创建
目录 一、idea创建Spring Boot项目1.1 创建过程1.2 依赖下载问题 二、网页创建Spring Boot项目三、目录介绍四、运⾏项⽬,看是否创建成功4.1 请求响应流程分析 五、常见报错5.1 Whitelabel Error Page4.1.1 注解写错:5.1.2 500 ⽆法访问此⽹站 六、状态码…...
java垃圾回收机制介绍
Java垃圾回收机制(Garbage Collection, GC)是Java编程语言中的一项重要特性,它自动管理内存,释放不再使用的对象 1. 堆(Heap): • Java虚拟机(JVM)中用于存储对象实例的内…...
计算机视觉硬件知识点整理六:工业相机选型
文章目录 前言一、工业数字相机的分类二、相机的主要参数三、工业数字摄像机主要接口类型四、选择工业相机的考量因素六、实例分析 前言 随着科技的不断进步,工业自动化领域正经历着前所未有的变革。作为工业自动化的重要组成部分,工业相机在工业检测、…...
一种多功能调试工具设计方案开源
一种多功能调试工具设计方案开源 设计初衷设计方案具体实现HUB芯片采用沁恒微CH339W。TF卡功能网口功能SPI功能IIC功能JTAG功能下行USB接口 安路FPGA烧录器功能Xilinx FPGA烧录器功能Jlink OB功能串口功能RS232串口RS485和RS422串口自适应接口 CAN功能烧录器功能 目前进度后续计…...
鸿蒙NEXT开发笔记(十九)仿抖音快手App的相对布局
上一节我们利用photoAccessHelper实现了从相册挑选视频的功能,但在短视频APP界面,视频画面上还叠加了若干交互图标,包括但不限于:点赞、收藏、评论、分享等等。那么在某个组件上叠加显示其他组件,就用到了相对布局这个…...
微信小程序px和rpx单位互转方法
js代码如下 Page({data: {width: 0,width2: 0},onLoad: function (options) {let px this.pxToRpx(380)let rpx this.rpxToPx(730.7692307692307) // 检查两个互转是否是相同即可,例如pxToRpx(380)转成730.7692307692307 则rpxToPx(730.7692307692307)如果是380则代表互转没…...
WPF+LibVLC开发播放器-LibVLC在C#中的使用
LibVLC在C#中的使用 安装包Nuget使用控件使用播放器初始化加载视频文件 视频教程: 使用WPFLibVLC快速开发一个播放器 安装包Nuget 安装下面两个包,必须安装两个 一个是相关框架对应的包,Winform就安装LibVLCSharp.Winform;WPF就安装LibVLCSharp.WPF&am…...
【Unity基础】Unity中Transform.forward的详解与应用
在Unity中,Transform.forward 是一个常用属性,它表示物体的“前方”方向,即物体本地坐标系中 Z 轴(蓝色轴)在世界坐标系中的方向。它动态反映物体的旋转情况,非常适合用于移动、检测、方向控制等场景。 什么…...
01-树莓派基本配置-基础配置配置
树莓派基本配置 文章目录 树莓派基本配置前言硬件准备树莓派刷机串口方式登录树莓派接入网络ssh方式登录树莓派更换国内源xrdp界面登录树莓派远程文件传输FileZilla 前言 树莓派是一款功能强大且价格实惠的小型计算机,非常适合作为学习编程、物联网项目、家庭自动化…...
Milvus×OPPO:如何构建更懂你的大模型助手
01. 背景 AI业务快速增长下传统关系型数据库无法满足需求。 2024年恰逢OPPO品牌20周年,OPPO也宣布正式进入AI手机的时代。超千万用户开始通过例如通话摘要、新小布助手、小布照相馆等搭载在OPPO手机上的应用体验AI能力。 与传统的应用不同的是,在AI驱动的…...
《Python基础》之Pandas库
目录 一、简介 二、Pandas的核心数据结构 1、Series 2、DataFrame 三、数据读取与写入 1、数据读取 2、数据写入 四、数据清洗与处理 1、处理缺失值 2、处理重复值 3、数据转换 五、数据分析与可视化 1、统计描述 2、分组聚合 3、数据可视化 六、高级技巧 1、时…...
LeetCode Hot100 31~40
链表 31. K个一组翻转链表 题目不难理解 主要是怎么写出清晰易懂的代码 可以先分成K组 再排序 class Solution { public:ListNode* reverseKGroup(ListNode* head, int k) {ListNode* dummyHead new ListNode();dummyHead->next head;// 首先查看需要翻转几次int count…...
C语言(分支练习)
1.输⼊你的⾝⾼和体重,测试你的健康状况。 计算bmi的值, bmi (体重(kg)/⾝⾼(m)的平⽅) 如果bmi ⼩于18.5, 则显⽰“偏瘦,注意加强营 养” 如果bmi 在18.5和23.9之间&a…...
指针(上)
目录 内存和地址 指针变量和地址 取地址(&) 解引用(*) 大小 类型 意义 const修饰 修饰变量 修饰指针 指针运算 指针- 整数 指针-指针 指针的关系运算 野指针 概念 成因 避免 assert断言 指针的使用 strl…...
(笔记)vue3使用Element Plus全局引入icons
如果你想要在项目中直接使用 Element Plus 的图标 icons,而不需要在每次使用的时候都要引入,那么你可以参考本文。 1、图标下载 在使用 Element Plus 的图标前,需要先下载图标库。 npm $ npm install element-plus/icons-vue yarn $ ya…...
基于Matlab卡尔曼滤波的GPS/INS集成导航系统研究与实现
随着智能交通和无人驾驶技术的迅猛发展,精确可靠的导航系统已成为提升车辆定位精度与安全性的重要技术。全球定位系统(GPS)和惯性导航系统(INS)在导航应用中各具优势:GPS提供全球定位信息,而INS…...
大学阶段matlab期末考试所有一定会考的函数及其基本格式
1.矩阵操作相关函数 zeros函数 基本功能:创建全零矩阵。基本格式:A zeros(m,n),其中m和n分别表示矩阵的行数和列数,例如创建一个3X4的全零矩阵A,可以写为A zeros(3,4)。 ones函数 基本功能:创建全 1 矩…...
【linux】(23)对象存储服务-MinIo
MinIO 是一个高性能的对象存储服务,兼容 Amazon S3 API。 Docker安装MinIo 前提条件 确保您的系统已经安装了 Docker。如果还没有安装 Docker,可以参考 Docker 官方文档进行安装。 1. 拉取 MinIO Docker 镜像 首先,从 Docker Hub 拉取 Mi…...
Nginx学习-安装以及基本的使用
一、背景 Nginx是一个很强大的高性能Web和反向代理服务,也是一种轻量级的Web服务器,可以作为独立的服务器部署网站,应用非常广泛,特别是现在前后端分离的情况下。而在开发过程中,我们常常需要在window系统下使用Nginx…...
前端:localstorage, session
学习帖子 —总结— 要快速掌握 localStorage 和 sessionStorage 的核心知识,以下是最重要的20%,这将帮助你理解和操作80%的功能: 基本概念: localStorage 和 sessionStorage 是 Web Storage API 的一部分,用于在客户端…...
剖析一下自己的简历第二条
剖析一下自己的简历第二条 背景前置说明可能会被问到的问题 背景 剖析一下自己简历, 增加对一些专业知识的掌握. 我的简历第二条是这样写的: “2. 熟悉JVM、JMM,包括内存模型,垃圾回收机制,了解其基本调优技巧并具备线上调优经验。”. 前置…...
利用Docker一键发布Nginx-Tomcat-MySQL应用集群
Docker简介,可以看上一篇文章: 为什么互联网公司离不开Docker容器化,它到底解决了什么问题?-CSDN博客 Docker体系结构 docker核心就是镜像和容器: 镜像就是应用程序的安装文件,包含了所有需要的资源&…...
九,[极客大挑战 2019]LoveSQL1
进入靶场 随便输输 试试字符 报错了 根据以上4张图知是单引号闭合 还以为这是flag呢,白激动一场,,,,,,,,自闭了 有个搞笑的点,其实可以用sqlmap 不过咱不用哈…...
17 go语言(golang) - 错误处理
错误处理 错误处理是编程中用于识别、响应和恢复程序运行时出现的错误和异常情况的过程。其目的是确保程序的鲁棒性(一个系统、模型或函数在面对错误输入、工作压力、意外情况或故意攻击时仍能保持稳定性和可靠性的能力),即使在出现错误的情…...
前端跳转路由的时候,清掉缓存
清除路由缓存的方法 使用 $router.push() 方法:在跳转路由时,可以通过传递一个包含 replace: true 属性的对象来实现清除路由缓存。例如: this.$router.push({ path: "/new-route", replace: true }); 使用 $router.replace…...
CentOS使用chrony服务进行时间同步源设置脚本
CentOS使用chrony服务进行时间同步源设置脚本 #!/bin/bash# Created: 2024-11-26 # Function: Check and Set OS time sync source to 10.0.11.100 # FileName: centos_set_time_source_to_ad.sh # Creator: Anster # Usage: # curl http://webserver-ip/scripts/centos_set…...
AI RPA 影刀基础教程:开启自动化之旅
RPA 是什么 RPA 就是机器人流程自动化,就是将重复的工作交给机器人来执行。只要是标准化的、重复的、有逻辑行的操作,都可以用 RPA 提效 准备 安装并注册影刀 影刀RPA - 影刀官网 安装 Chrome 浏览器 下载链接:Google Chrome 网络浏览器 …...
深入解析Java集合框架:List集合及其实现类的应用与原理
|| 持续分享系列教程,关注一下不迷路 || || B站视频教程:墨轩大楼 || || 知识星球:墨轩编程自习室 || Java集合框架是Java编程语言中一个非常重要的组成部分,…...
MySQL需掌握到何种程度?才能胜任工作
大家好,我是袁庭新。星友问:MySQL需要学到什么程度?才能胜任日常的软件开发工作呢!以下是一些建议的学习目标和程度,这些目标旨在帮助你在工作中高效地使用MySQL。 数据库的基本概念、MySQL的安装及配置、SQL的概念、S…...
如何使用brew安装phpredis扩展?
如何使用brew安装phpredis扩展? phpredis扩展是一个用于PHP语言的Redis客户端扩展,它提供了一组PHP函数,用于与Redis服务器进行交互。 1、cd到php某一版本的bin下 /usr/local/opt/php8.1/bin 2、下载 phpredis git clone https://githu…...
力扣hot100道【贪心算法后续解题方法心得】(三)
力扣hot100道【贪心算法后续解题方法心得】 十四、贪心算法关键解题思路1、买卖股票的最佳时机2、跳跃游戏3、跳跃游戏 | |4、划分字母区间 十五、动态规划什么是动态规划?关键解题思路和步骤1、打家劫舍2、01背包问题3、完全平方式4、零钱兑换5、单词拆分6、最长递…...
时间同步服务器--Linux中
时间同步服务器 1. 时间同步服务 时间同步:多主机协作工作时,各个主机的时间同步很重要,时间不一致会造成很多重要应用的故障,如:加密协议,日志,集群等,利用NTP(Network Time Protocol )协议使网络中的各…...
银河麒麟V10-SP1设置redis开机自启
前言: redis安装请看:银河麒麟V10-SP1离线安装redis5.0.1_银河麒麟v10 redis5.0-CSDN博客 一、编辑自启文件 vim /etc/systemd/system/redis.service [Unit] DescriptionRedis In-Memory Data Store Afternetwork.target [Service] Typeforking ExecS…...
JVM 之垃圾回收器
一、GC 的分类 1.1 串行 VS 并行 串行回收:指在同一时间段内只允许有一个 CPU 用于执行垃圾回收操作,此时工作线程被暂停,直至垃圾回收结束 在单 CPU 处理器或者较小的应用内存等硬件平台不是特别优越的场合,串行回收器的超过并…...
基于Java Springboot宠物咖微信小程序
一、作品包含 源码数据库全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue、Element-ui 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA/eclipse 微信开发者工具 数…...
求助——AssertionError: Attribute pipeline is missing from configuration.json.
我在本地运行Sunsimiao大模型的时候遇到了“AssertionError: Attribute pipeline is missing from configuration.json.”的问题。在网上找了很多问题都没有解决,求助一下广大网友。有什么好的解决方法吗? 本地环境如上所示,不知是哪里出…...
LearnOpenGL学习(光照 -- 颜色,基础光照,材质,光照贴图)
光照 glm::vec3 lightColor(0.0f, 1.0f, 0.0f); glm::vec3 toyColor(1.0f, 0.5f, 0.31f); glm::vec3 result lightColor * toyColor; // (0.0f, 0.5f, 0.0f); 说明:当我们把光源的颜色与物体的颜色值相乘,所得到的就是这个物体所反射的颜色。 创建…...