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

今天你学C++了吗?——string(上)


♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥

♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥

♥♥♥我们一起努力成为更好的自己~♥♥♥

♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥

♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥

✨✨✨✨✨✨ 个人主页✨✨✨✨✨✨

前面的一篇博客,我们已经了解了STL的概念,这一篇博客我们开始STL的具体内容——string

准备好了吗~我们发车啦~🚗🚗🚗🚗🚗🚗

目录

auto和范围for

auto关键字

范围for

string类

构造

数据遍历

下标+[ ]

迭代器(通用的容器访问方式)

范围for

Capacity

数据访问

数据修改

push_back和pop_back

append

operator+=

insert

erase

assign

replace

swap

数据查找

运算符重载

relational operators (string)

operator + / << / >>

getline


auto和范围for

首先我们来补充 2 C++11 的小语法,方便我们后面的学习~

auto关键字

早期 C/C++ auto 的含义是:使用 auto 修饰的变量,是具有自动存储器的局部变量 。使用auto替换长类型就十分方便~
C++11 中,标准委员会变废为宝赋予了 auto 全新的含义即: auto 不再是一个存储类型 指示符,而是 作为一个新的类型指示符来指示编译器 auto 声明的变量必须由编译器在编译时期 推导而得
用auto 声明指针类型时,用 auto auto* 没有任何区别,但用 auto 声明引用类型时则必须加 &
当在 同一行声明多个变量 时,这些 变量必须是相同的类型 ,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量
C++11 auto不能作为函数的参数(后面C++20支持)
auto可以做返回值,建议谨慎使用
auto不能直接用来声明数组

这里我们结合具体的例子来看看auto的使用~

#include<iostream>
using namespace std;//void test1(auto a)//err——C++11 auto不能作为函数的参数
//{
//	//....
//}auto test2()//auto可以做返回值,建议谨慎使用 
{return 10;
}
int main()
{//auto根据右边的数据类型推导得到int a = 1;auto b = a;//auto和auto*都可以声明指针类型auto c = &a;//auto右边可以是指针类型,也可以是其他类型auto* d = &a;//auto*右边必须是指针类型//用auto声明引用类型时则必须加&auto& e = a;auto aa = 1, bb = 2;//auto cc = 3, dd = 4.4;//err——在声明列表中,auto必须始终推导为同一类型//auto f;//err——无法推导类型//auto a[] = { 1,2,3,4,5 };//err——auto不能直接用来声明数组cout << test2() << endl;return 0;
}

谨慎使用 auto做返回值!!!

例:

#include<iostream>
using namespace std;
auto func1()
{//......return 1;
}auto func2()
{//......return func1();
}auto func3()
{//......return func2();
}auto func4()
{//......return func3();
}
int main()
{auto ret = func4();return 0;
}

当上面的代码想要知道ret的类型,就需要知道func4()返回值的类型,层层寻找直到func1()才能够知道类型,当程序十分复杂的时候,就不方便~所以我们要谨慎使用 auto做返回值!!!

范围for

对于一个 有范围的集合 而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误~
因此 C++11 中引入了基于范围的 for 循环~
for循环后的括号由冒号 分为两部分:第一部分是范围 内用于迭代的变量,第二部分则表示被迭代的范围 ,自动迭代,自动取数据,自动判断结束。

按值拷贝

在范围for循环中,每次迭代到的元素都会被按值拷贝到循环变量中~这意味着,如果对循环变量进行修改,不会影响到容器中的原始元素如果需要修改容器中的元素,可以通过在循环变量前加上引用符号&来实现。这样,循环变量就会直接引用容器中的元素,从而允许在循环体内对元素进行修改。

范围for 可以作用到数组和容器对象上进行遍历
范围for 的底层比较简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到~
接下来,我们来看看范围for的使用例子
#include<iostream>
#include<string>
using namespace std;
int main()
{int a[] = { 1,2,3,4,5,6 };// C++98的遍历cout << "C++98的遍历" << endl;for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i){a[i] *= 2;}for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i){cout << a[i] << " ";}cout << endl;// C++11的遍历cout << "C++11的遍历" << endl;//使用范围forfor (auto& e : a)//传引用——修改数据修改数组里面元素{e *= 2;}for (auto e : a)//传参——自动取数据赋值给e{cout << e << " ";//打印e}cout << endl;for (auto e : a)//传参——调用拷贝构造,无法修改数组里面元素{e *= 2;}for (auto e : a)//传参——自动取数据赋值给e{cout << e << " ";//打印e}cout << endl;string str("Hello World!");for (auto ch : str){cout << ch << " ";}cout << endl;//使用int/charcout << "使用int/char" << endl;//也可以使用int/char,类型匹配就可以了// 使用auto更加方便——自动取数据推导类型for (int e : a){cout << e << " ";}cout << endl;for (char e : str){cout << e << " ";}cout << endl;return 0;
}

我们可以发现使用范围for遍历数组和容器更加方便快捷!~

string类

学习string类,我们就需要查阅文档,小编这里在cplusplus官方网站上查阅string类的接口~

string类的头文件是string

这里介绍几个比较重要的接口~

构造

查阅文档:

构造比较重要的接口就是 无参构造、有参构造、拷贝构造 ~

我们包头文件来简单使用一下~

#include<iostream>
#include<string>//string类头文件
using namespace std;
int main()
{string s1;//调用无参构造string s2("Hello Xiaodu!!!");//调用有参构造string s3(s2);//调用拷贝构造string s4(s2, 6, 100);//取一部分进行拷贝进行初始化//从第6个位置开始,拷贝100个字符,如果没有100个取到字符串末尾就可以了string s5(6, 'd');//使用6个字符‘d’进行初始化string s6(s2, 6);//从第6个位置开始拷贝字符直到末尾//string 支持流插入、流提取运算符,我们可以直接使用cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;cout << s4 << endl;cout << s5 << endl;cout << s6 << endl;return 0;
}

数据遍历

下标+[ ]

string类对[ ]也进行了运算符重载,所以我们这里也就可以直接使用下标 +[ ]进行遍历和修改~

我们也可以发现,它重载了普通版本和const版本~使用的时候会匹配最合适的~

#include<iostream>
#include<string>//string类头文件
using namespace std;
void test1()
{string s1("Hello Xiaodu!!!");cout << s1 << endl;//遍历+修改//下标 + []for (size_t i = 0; i < s1.size(); i++){s1[i] += 1;//实现修改}//范围forfor (auto e : s1){cout << e << " ";//打印出来}cout << endl;//const 修饰const string s2("hhhhhh");//下标 + []//for (int i = 0; i < s2.size(); i++)//{//	s2[i] += 1;//const修饰,不可以实现修改//}for (size_t i = 0; i < s2.size(); i++){cout << s2[i] << " ";//可以访问打印}cout << endl;
}
int main()
{test1();return 0;
}

迭代器(通用的容器访问方式)

迭代器是一个行为像指针的对象~迭代器模式实现集合遍历,隐藏内部细节,提供统一接口~

迭代器是一个左闭右开的区间—— [ )

begin() 返回第一个位置的迭代器

end()不是最后一个位置的迭代器,而是最后一个位置的迭代器

我们知道字符串末尾的‘\0’是字符串结束标志,并不是有效字符~

迭代器同样分为两种,一种是普通迭代器——可读可写,一种是const 迭代器——可读不可写~

例:

void test2()
{string s1("AAAAAA");cout << s1 << endl;//使用迭代器遍历+修改//迭代器是一个左闭右开的区间//[ )string::iterator it1 = s1.begin();//begin() 返回第一个位置的迭代器while (it1 != s1.end())//end()不是最后一个位置的迭代器{//类似于指针的使用——这里遍历+修改//普通迭代器可读可写*it1 += 1;cout << *it1 << " ";++it1;//迭代器——行为像指针的对象}cout << endl;//const 修饰的迭代器可读不可写//const string::iterator it2 = s1.begin();//err 错误的const修饰,这里是修饰迭代器本身,但是遍历需要++//我们希望里面的内容不可以修改string::const_iterator it2 = s1.cbegin();//string类里面提供了const_iteratorwhile (it2 != s1.cend()){//*it2 += 1; //const_iterator不可以修改cout << *it2 << " ";++it2;}cout << endl;}

画图理解:

同时还有反向迭代器——反过来访问

//反向迭代器
string::reverse_iterator rit = s1.rbegin();
while (rit != s1.rend())
{*rit += 1;cout << *rit << " ";++rit;
}

画图理解:

具体实现可能会因为不同的容器有一些不一样~

范围for

我们也可以使用范围for遍历和修改~需要注意的是我们前面提到过范围for是按值拷贝,在循环变量前加上引用符号&才可以修改容器中的元素

void test3()
{string s1("AAAAAA");for (auto e : s1){e += 1;cout << e << " ";}cout << endl;cout << s1 << endl;//在循环变量前加上引用符号&才可以修改容器中的元素for (auto& e : s1){e += 1;cout << e << " ";}cout << endl;cout << s1 << endl;
}

Capacity

string类里面有很多的关于容量的接口~我们一起来看看~

  • length() / size()
    • 返回字符串的当前有效长度(字符数)~(不包括‘\0’)
    • 这两个函数在功能上是等价的~
  • max_size()
    • 返回字符串对象能包含的最大字符数~
    • 这通常受可用内存和系统限制的影响~
  • capacity()
    • 返回字符串在不分配更多内存的情况下能包含的最大字符数~(不包括‘\0’)
    • 这可能大于或等于字符串的当前长度~
  • reserve(n)
    • 请求改变字符串的容量至少为n个字符~
    • 如果n小于或等于当前容量,则不做任何操作~
    • 这可以用于优化性能,通过减少内存重新分配的次数~
  • shrink_to_fit()
    • 缩容:请求减少字符串的容量以匹配其当前长度~
    • 这不会改变字符串的内容或长度,但可能会释放未使用的内存~
  • empty()
    • 检查字符串是否为空(即长度是否为0)~
    • 返回一个布尔值~
  • clear()
    • 清除字符串的内容,使其变为空字符串~
    • 不改变字符串的容量~
  • resize()
    • 调整字符串的长度到指定的新大小。
    • 如果新大小小于当前长度,则字符串会被截断~
    • 如果新大小大于当前长度,则会在字符串末尾添加指定数量的字符(默认为空字符,但实际表现可能不显式显示这些空字符,且依赖于实现可能会用空格或其他字符填充)
    • 不直接改变字符串的底层存储容量(尽管如果新大小远大于当前容量,内部可能会重新分配内存)

下面我们来进行简单测试,有些具体的内容,我们会在底层实现的时候更加清晰~

#include<iostream>
#include<string>//string类头文件
using namespace std;
int main()
{string s1("AAAAAA");cout << "s1 " << s1 << endl;//字符串的有效长度cout << "s1.size() = " << s1.size() << endl;cout << "s1.length() = " << s1.length() << endl;//返回字符串对象能包含的最大字符数cout << " s1.max_size() = " << s1.max_size() << endl;//返回字符串在不分配更多内存的情况下能包含的最大字符数cout << "s1.capacity() = " << s1.capacity() << endl;//检查字符串是否为空(即长度是否为0)cout << "s1.empty() = " << s1.empty() << endl;//缩容:请求减少字符串的容量以匹配其当前长度string s2(100, 'A');cout << "before s2.shrink_to_fit(),s2.capacity() = " << s2.capacity() << endl;s2 = s1;s2.shrink_to_fit();cout << "after s1.shrink_to_fit(),s2.capacity() = " << s2.capacity() << endl;//清除字符串的内容,使其变为空字符串,不改变字符串的容量s1.clear();cout << "s1 = " << s1 << endl;cout << "s1.size() = " << s1.size() << endl;cout << "s1.capacity() = " << s1.capacity() << endl;return 0;
}

接下来,我们来看看比较特殊的reserve的使用~

  • reserve(n)
    • 请求改变字符串的容量至少为n个字符~不会修改数据
    • 如果n小于或等于当前容量,则不做任何操作~(具体平台具体分析)
    • 这可以用于优化性能,通过减少内存重新分配的次数(减少扩容,提高效率)

#include <iostream>
#include <string>
using namespace std;
int main()
{//vs2022 测试string s1("AAAAAA");cout << s1.size() << endl;cout << s1.capacity() << endl;//当n小于等于capacity的时候,capacity不会发生变化//也就是不会进行缩容s1.reserve(5);cout << s1.size() << endl;cout << s1.capacity() << endl;s1.reserve(15);cout << s1.size() << endl;cout << s1.capacity() << endl;//希望容量为16,事实上容量变为31,会多给一些//不同平台处理情况不一样s1.reserve(16);cout << s1.size() << endl;cout << s1.capacity() << endl;s1.reserve(25);cout << s1.size() << endl;cout << s1.capacity() << endl;s1.reserve(36);cout << s1.size() << endl;cout << s1.capacity() << endl;//size不会变化return 0;
}

提供了这个接口,在不同平台下,结果可能会不一样~比如在g++平台下,当n小于capacity的时候就会进行缩容~

有修改容量的肯定也就有修改字符串长度的——resize

  • resize()
    • 调整字符串的长度到指定的新大小。
    • 如果新大小小于当前长度,则字符串会被截断~
    • 如果新大小大于当前长度,则会在字符串末尾添加指定数量的字符(默认为空字符,但实际表现可能不显式显示这些空字符,且依赖于实现可能会用空格或其他字符填充)
    • 不直接改变字符串的底层存储容量(尽管如果新大小远大于当前容量,内部可能会重新分配内存)

我们可以调用两种函数~我们一起来看看它的使用~

#include <iostream>
#include <string>
using namespace std;
int main()
{//vs2022 测试string类的resizestring s1("AAAAAA");cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;//n比原来的size小,删除数据s1.resize(5);cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;//n比原来的size大,但是小于容量// 插入数据——vs平台默认为空字符,不会显示出来s1.resize(12);cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;//n比原来的size大,并且大于原来容量,插入数据并且扩容s1.resize(25);cout << s1 << endl;cout << s1.size() << endl;cout << s1.capacity() << endl;//另外一种函数调用string s2("BBBBBB");cout << s2 << endl;cout << s2.size() << endl;cout << s2.capacity() << endl;s2.resize(5, 'c');cout << s2 << endl;cout << s2.size() << endl;cout << s2.capacity() << endl;//插入数据,插入我们想要的字符s2.resize(12, 'c');cout << s2 << endl;cout << s2.size() << endl;cout << s2.capacity() << endl;s2.resize(25, 'c');cout << s2 << endl;cout << s2.size() << endl;cout << s2.capacity() << endl;return 0;
}

数据访问

[ ]这个运算符重载相信就不用多说了,我们来看看一个我们以前没有见过的——at

事实上,at与[ ]的使用是类似的~

在C++的std::string类中,成员函数at用于访问字符串中指定位置的字符~

operator[]不同的是,如果指定的位置超出了字符串的范围,at会抛出一个std::out_of_range异常,而operator[]则可能导致未定义行为(例如访问越界内存),会直接断言报错~

例:

#include <iostream>
#include <string>
#include <stdexcept> // for std::out_of_rangeint main() {std::string str = "Hello, World!";// 使用at访问字符串中的字符try {char ch = str.at(7); // 获取索引为7的字符,即 'W'std::cout << "Character at index 7: " << ch << std::endl;// 尝试访问超出范围的索引char out_of_range_ch = str.at(20); // 这将抛出std::out_of_range异常}catch (const std::out_of_range& e){std::cerr << "Error: " << e.what() << std::endl; // 输出异常信息}return 0;
}

我们来看看back和front,看起来这个名字我们就知道它可以访问字符串的第一个和最后一个字符,我们需要注意的是末尾的‘\0'不是有效字符~

#include <iostream>
#include <string>
using namespace std;
int main()
{string s1("Hello,World!");//访问字符串第一个字符cout << "s1.front() " << s1.front() << endl;//访问字符串最后一个字符cout << "s1.back() " << s1.back() << endl;//末尾的\0不是有效字符return 0;
}

数据修改

push_back和pop_back

看这名字,我们就可以很清楚的知道这是用来尾插和尾删一个字符的~

例:

void test1()
{string s1("HHHHHH");cout << s1 << endl;//尾插一个字符s1.push_back('H');cout << s1 << endl; //尾删一个字符s1.pop_back();cout << s1 << endl;
}

append

append我们一样可以用来string添加字符~

我们来看看使用:

//append
void test2()
{string s1("HHHHHH");cout << s1 << endl;string s2("ABCDEF");//s1末尾添加字符串s1.append("AAA");cout << s1 << endl;//s1末尾添加字符串的前面2个字符s1.append("AAA", 2);cout << s1 << endl;//取s2的一部分进行插入s1.append(s2, 3, 2);//向s1插入第三个位置后面的两个字符cout << s1 << endl; //s1末尾添加2个字符Gs1.append(2, 'G');cout << s1 << endl;
}

这几个比较简单,我们来看看它还可以支持插入一个迭代器区间~

void test3()
{string s1;s1.push_back('A');s1.push_back('A');s1.push_back('A');s1.push_back('A');s1.append("BBBB");cout << s1 << endl;string s2("abcdefg");//取一部分s1.append(s2.begin() + 3, s2.end());cout << s1 << endl;s1.append(s2.begin(), s2.end());cout << s1 << endl;
}

operator+=

+=运算符的重载也让我们修改字符串更加方便~

void test4()
{string s1("AAAAAA");cout << s1 << endl;s1 += "abc";cout << s1 << endl;s1 += 'e';cout << s1 << endl;string s2("BBB");s1 += s2;cout << s1 << endl;
}

insert

从这个名字就可以知道,它是用来插入数据的~我们来看看它具体的接口~

我们可以发现insert的接口也是十分丰富的,我们一下子肯定也记不住,最好的办法是边学边记,在实践中学会查文档,加深理解记忆~

这里我们来简单使用几个~

void test5()
{string s1("AAAAAA");cout << s1 << endl;s1.insert(s1.begin(), 'B');//支持迭代器cout << s1 << endl;s1.insert(s1.end(), 'B');//支持迭代器cout << s1 << endl;s1.insert(3, "CCC");cout << s1 << endl;s1.insert(1, "D");cout << s1 << endl;
}

erase

有插入数据,那么就有删除数据了~

erase的接口就比较少,我们来简单使用一下~

void test6()
{string s1("ABCDEFG");cout << s1 << endl;s1.erase(s1.begin());//同样支持迭代器cout << s1 << endl;s1.erase(s1.end() - 1);//同样支持迭代器cout << s1 << endl;s1.erase(1, 2);//删除第一个位置后面两个字符//使用下标更好理解——删除从下标为1开始的两个字符cout << s1 << endl;s1.erase(1);//使用默认参数,删除第一个位置后面的所有字符//删除从下标为1开始的后面所有字符cout << s1 << endl;
}

assign

assign类似于赋值操作,但是它的接口多样,可以满足我们的不同需求,我们来看看它们的接口~

void test7()
{string s1("ABCDEF");string s2("BBBBBB");string s3("CCCCCC");string s4("DDDDDD");cout << "s1:" << s1 << endl;cout << "s2:" << s2 << endl;cout << "s3:" << s3 << endl;cout << "s4:" << s4 << endl;//普通赋值s2 = s1;//assign——接口更加多样s3.assign(s1); s4.assign(s1.begin(), s1.end() - 3);cout << "s1:" << s1 << endl;cout << "s2:" << s2 << endl;cout << "s3:" << s3 << endl;cout << "s4:" << s4 << endl;}

replace

replace我们知道有代替的意思,C++里面的striing类的replace也就是可以进行替换,我们来看看它丰富的接口~

void test8()
{string s1("ABCDEF");string s2("BBBBBB");string s3("CCCCCC");string s4("DDDDDD");cout << "s1:" << s1 << endl;cout << "s2:" << s2 << endl;cout << "s3:" << s3 << endl;cout << "s4:" << s4 << endl;s2.replace(1, 3, s1);//从下标为1的位置开始的3个字符替换成s1s3.replace(s3.begin(), s3.end(), s1);s4.replace(1, 4, 3, 'X');//从下标为1的位置开始的4个字符替换成3个X字符cout << "s1:" << s1 << endl;cout << "s2:" << s2 << endl;cout << "s3:" << s3 << endl;cout << "s4:" << s4 << endl;
}

replace接口十分丰富,在我们需要的时候查文档就好了,同时需要注意谨慎使用replace,因为它涉及到数据的移动,它的效率也是比较低的~

swap

swap也就是交换字符串~在后面底层实现的时候我们会更加清楚~

void test9()
{string s1("AAA");string s2("BBBBBB");cout << "s1:" << s1 << endl;cout << "s2:" << s2 << endl;s2.swap(s1);cout << "s1:" << s1 << endl;cout << "s2:" << s2 << endl;
}

数据修改的接口就告一段落了,还是一样,它们的接口很多,我们不需要全部熟记,在需要的时候查一查文档也是没有问题的~

数据查找

  1. c_str()
    • 功能:获取C风格字符串的等价物~
    • 说明:返回一个指向以空字符结尾的字符数组的指针,该数组包含了与string对象相同的字符序列,这个指针可以用于需要C风格字符串的API调用~
  2. data()
    • 功能:获取字符串数据
    • 说明:类似于c_str(),返回一个指向字符串内部数据(不包括终止的空字符)的指针。与c_str()不同的是,data()不保证返回的指针指向以空字符结尾的数组,但实际上在C++11及更高版本中,返回的指针确实是以空字符结尾的。
  3. get_allocator()
    • 功能:获取分配器
    • 说明:返回与string对象关联的分配器对象的副本。分配器用于管理内存分配和释放。
  4. copy()
    • 功能:从字符串复制字符序列。
    • 说明:将指定数量的字符从string对象复制到另一个字符数组中。这个函数通常用于需要直接操作字符数组的场景。
  5. find()
    • 功能:在字符串中查找内容。
    • 说明:搜索子字符串或字符在string对象中的位置。如果找到,返回子字符串或字符第一次出现的位置的索引(下标);否则,返回string::npos
  6. rfind()
    • 功能:查找字符串中内容的最后一次出现。
    • 说明:类似于find(),但返回子字符串或字符最后一次出现的位置的索引(下标)
  7. find_first_of()
    • 功能:在字符串中查找第一个匹配的字符。
    • 说明:在给定的字符集(或子字符串)中搜索string对象中第一个出现的字符,并返回其位置索引。
  8. find_last_of()
    • 功能:从字符串末尾开始查找第一个匹配的字符。
    • 说明:类似于find_first_of(),但从string对象的末尾开始搜索。
  9. find_first_not_of()
    • 功能:在字符串中查找第一个不匹配的字符。
    • 说明:在给定的字符集(或子字符串)之外搜索string对象中第一个出现的字符,并返回其位置索引。
  10. find_last_not_of()
    • 功能:从字符串末尾开始查找第一个不匹配的字符。
    • 说明:类似于find_first_not_of(),但从string对象的末尾开始搜索。
  11. substr()
    • 功能:生成子字符串。
    • 说明:返回从指定位置开始、指定长度的子字符串。如果未指定长度,则返回从起始位置到字符串末尾的所有字符。
  12. compare()
    • 功能:比较字符串。
    • 说明:将string对象与另一个string对象或C风格字符串进行比较。根据比较结果返回负值、零或正值,分别表示小于、等于或大于。

测试:

void test1()
{string s1("AAAAAA");string s2("AAAAAA");if (s1.c_str() == s2.c_str())cout << "true" << endl;elsecout << "false" << endl;if (s1.data() == s2.data())cout << "true" << endl;elsecout << "false" << endl;
}

void test2()
{string s1("Hello World!  Hello Xiaodu!");cout << s1.find('!') << endl;//默认从下标为0的位置开始找cout << s1.find("Hello") << endl;//默认从下标为0的位置开始找cout << s1.rfind('!') << endl;//倒着找cout << s1.rfind("Hello") << endl << endl;cout << s1.find_first_of('!') << endl;cout << s1.find_last_of('!') << endl;cout << s1.find_first_not_of('!') << endl;cout << s1.find_last_not_of('!') << endl;}

copy和substr的使用

void test3() 
{//copy从字符串复制字符序列char buffer[20];std::string str("Test string...");//从位置为5的字符开始复制8个字符到bufferstd::size_t length = str.copy(buffer, 8, 5);buffer[length] = '\0';std::cout << "buffer contains: " << buffer << '\n';//substr生成子字符串string s1 = "Hello Xiaodu!";//返回从指定位置开始、指定长度的子字符串//从位置为6的字符开始复制7个字符给s2string s2 = s1.substr(6, 7);cout << s1 << endl;cout << s2 << endl;
}

运算符重载

比较字符串除了compare以外,还有一些比较关系的运算符重载

relational operators (string)

使用:

void test4()
{//compare的使用string s1 = "Hello Xiao!";string s2 = "Hello Dary!";if (s1.compare(s2)){cout << "s1 > s2" << endl;}else{cout << "s1 < s2" << endl;}//比较运算符重载if (s1 > s2){cout << "s1 > s2" << endl;}else{cout << "s1 < s2" << endl;}string s3("Ada!");if (s1 < s3){cout << "s1 < s3" << endl;}else{cout << "s1 > s3" << endl;}
}

operator + / << / >>

这些运算符重载相信也就不用多说了,我们进行直接使用~

void test5()
{string s1 = "Hello Xiaodu!";string s2 = "!!";string s3 = s1 + s2;cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;
}

getline

getline主要作用是从输入流中读取一行文本,直到遇到换行符(\n),也可以我们自己选择读取结束的字符,换行符本身不会被包含在读取的字符串中getline通常与std::string对象一起使用,因为它能够自动调整字符串的大小以适应读取的内容~

接下来,我们进行简单的使用:

1.

void test6()
{// 创建一个string对象来存储输入的行string inLine;// 提示用户输入一行文本cout << "请输入一行文本(按Enter结束输入): ";// 使用getline从标准输入读取一行文本,直到遇到换行符getline(cin, inLine);// 打印出用户输入的文本cout << "您输入的文本是: " << inLine << endl;
}

2.

void test7()
{// 创建一个string对象来存储输入的行string inLine;// 提示用户输入一行文本cout << "请输入一行文本(按#结束输入): ";// 使用getline从标准输入读取一行文本,直到我们选择的结束符getline(cin, inLine, '#');// 打印出用户输入的文本cout << "您输入的文本是: " << inLine << endl;
}

C++中string类的接口我们已经梳理得差不多了,我们后面的博客将会进行string类底层实现的讲解,让我们string类更加清晰~我们下次再见~


♥♥♥本篇博客内容结束,期待与各位优秀程序员交流,有什么问题请私信♥♥♥

♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥

✨✨✨✨✨✨个人主页✨✨✨✨✨✨


相关文章:

今天你学C++了吗?——string(上)

♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥ ♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥ ♥♥♥我们一起努力成为更好的自己~♥♥♥ ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥ ♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥ ✨✨✨✨✨✨ 个…...

使用通义万相Wan2.1进行视频生成

使用通义万相Wan2.1进行视频生成 源代码准备运行环境准备创建Python虚拟环境并激活安装依赖包 模型下载生成视频官网的视频生成例子简单描述场景视频生成示例详细描述场景视频生成示例 最近通义万相开源了其视频生成模型。模型有两个版本&#xff0c;一个是1.3B的&#xff0c;一…...

Windows在多网络下指定上网接口

Windows在多网络下指定上网接口 一、说明 设备情况&#xff1a;win11&#xff0c;同时连接了有线网和WLAN&#xff0c;有线网连接着NAS必须保持连接。需求&#xff1a;有些情况时&#xff0c;有线网无网络而WLAN有网&#xff0c;但系统仍走着有线导致无法上网。 二、方法 过…...

前端正则表达式完全指南:从入门到实战

文章目录 第一章&#xff1a;正则表达式基础概念1.1 什么是正则表达式1.2 正则表达式工作原理1.3 基础示例演示 第二章&#xff1a;正则表达式核心语法2.1 元字符大全表2.2 量词系统详解2.3 字符集合与排除 第三章&#xff1a;前端常用正则模式3.1 表单验证类3.1.1 邮箱验证3.1…...

【愚公系列】《Python网络爬虫从入门到精通》038-SQLite数据库

标题详情作者简介愚公搬代码头衔华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,亚马逊技领云博主,51CTO博客专家等。近期荣誉2022年度…...

npx degit 问题:could not fetch remote...

问题与处理策略 问题描述 npx degit dream-num/univer-sheet-start-kit univer-sheet-start-kit执行上述指令&#xff0c;报如下错误 ! could not fetch remote https://github.com/dream-num/univer-sheet-start-kit ! could not find commit hash for HEAD# 翻译无法获取远…...

空投与转账:Solana代币核心概念及代码实战精解

目录 1.空投和转账概念 1.1.空投(Airdrop) 1.2.转账(Transfer) 2.Solana 代币创建与空投核心代码总结 2.1. 代币创建核心代码 2.2. 添加元数据核心代码 2.3. 空投代币核心代码 3.效果图展示 1.空投和转账概念 1.1.空投(Airdrop) 创建新代币: 空投是铸造(Mint)新代币…...

Rust~tokio的AsyncRead

AsyncRead trait 定义 介绍 /// This trait is analogous to the [std::io::Read] trait, but integrates with /// the asynchronous task system. In particular, the [poll_read] method, /// unlike [Read::read], will automatically queue the current task for wakeup…...

NO.22十六届蓝桥杯备战|一维数组|七道练习|冒泡排序(C++)

B2093 查找特定的值 - 洛谷 题⽬要求下标是从0开始的&#xff0c;和数组的下标是吻合的&#xff0c;存放数据应该从下标0开始n的取值范围是1~10000数组中存放的值的绝对值不超10000&#xff0c;说明int类型就⾜够了找到了输出下标&#xff0c;找不到要输出-1&#xff0c;这⼀点…...

Linux下的网络通信编程

在不同主机之间&#xff0c;进行进程间的通信。 1解决主机之间硬件的互通 2.解决主机之间软件的互通. 3.IP地址&#xff1a;来区分不同的主机&#xff08;软件地址&#xff09; 4.MAC地址&#xff1a;硬件地址 5.端口号&#xff1a;区分同一主机上的不同应用进程 网络协议…...

【JavaWeb13】了解ES6的核心特性,对于提高JavaScript编程效率有哪些潜在影响?

文章目录 &#x1f30d;一. ES6 新特性❄️1. ES6 基本介绍❄️2. 基本使用2.1 let 声明变量2.2 const 声明常量/只读变量2.3 解构赋值2.4 模板字符串2.5 对象拓展运算符2.6 箭头函数 &#x1f30d;二. Promise❄️1. 基本使用❄️2. 如何解决回调地狱问题2.1回调地狱问题2.2 使…...

每日一题之屏蔽信号

问题描述 在与三体文明的对抗中&#xff0c;人类联邦探测到了两个重要的信号源&#xff0c;分别用非负整数 aa 和 bb 来表示。 为了抵御三体舰队的入侵&#xff0c;科学家们制定出一项关键策略——屏蔽信号&#xff0c;目标是要让 aa、bb 这两个信号源其中之一的数值归零。 在…...

TCP如何保证可靠性

目录 回顾TCP协议TCP报文头部 TCP如何保证可靠性&#xff1f;校验和序列号确认应答机制&#xff08;ACK&#xff09;超时重传机制连接管理机制&#xff08;三次握手和四次挥手&#xff09;流量控制滑动窗口滑动机制 拥塞控制慢开始 & 拥塞避免快重传 & 快恢复 回顾TCP协…...

HTTP协议深度解析:从Tomcat到HTTPS的全方位探索

目录 引言 一、Tomcat部署与Servlet容器 1.1 Tomcat部署流程 1.2 Tomcat与Servlet 二、HTTP协议的基础构成 2.1 URL 2.2 HTTP请求与响应 2.3 HTTP状态码 三、计算机网络与数据传输 3.1 局域网与路由器 3.2 IP地址与MAC地址 3.3 域名与DNS 四、HTTPS协议与数据加密…...

互联网医院实时数据监测智能分析系统设计概述(下)

阶段4:可视化仪表盘与用户界面开发 在这一阶段,我们将使用 Plotly Dash 来设计一个实时预测仪表盘,用于展示疾病传播趋势、医生评估信息等。我们还将实现实时数据更新与展示,确保数据能够及时反映系统中的变化。 1. 设计实时预测仪表盘 步骤 1:安装 Dash 依赖 首先,确…...

性能测试测试策略制定|知名软件测评机构经验分享

随着互联网产品的普及&#xff0c;产品面对的用户量级也越来越大&#xff0c;能抗住指数级增长的瞬间访问量以及交易量是保障购物体验是否顺畅的至关重要的一环&#xff0c;而我们的性能测试恰恰也是为此而存在的。 性能测试是什么呢&#xff1f;性能测试要怎么测呢&#xff1f…...

【AI+智造】在阿里云Ubuntu 24.04上部署DeepSeek R1 14B的完整方案

作者&#xff1a;Odoo技术开发/资深信息化负责人 日期&#xff1a;2025年2月28日 一、部署背景与目标 DeepSeek R1作为国产大语言模型的代表&#xff0c;凭借其强化学习驱动的推理能力&#xff0c;在复杂任务&#xff08;如数学问题、编程逻辑&#xff09;中表现优异。本地化部…...

蓝桥真题讲解

温馨提示&#xff1a;本系列文章非所有题都对对b组适用&#xff0c;b组的小伙伴请挑题看&#xff01; 目录 第一题 题目链接 题目解析 代码原理 代码编写 本题总结 第二题 题目链接 题目解析 代码原理 代码编写 本题总结 第三题 题目链接 题目解析 代码原理 …...

javaweb将上传的图片保存在项目文件webapp下的upload文件夹下

前端HTML表单 (upload.html) 首先&#xff0c;创建一个HTML页面&#xff0c;允许用户选择并上传图片。 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>图片上传</title> </head> <…...

北京大学DeepSeek与AIGC应用(PDF无套路下载)

近年来&#xff0c;人工智能技术飞速发展&#xff0c;尤其是大模型和生成式AI&#xff08;AIGC&#xff09;的突破&#xff0c;正在重塑各行各业的生产方式与创新路径。 北京大学联合DeepSeek团队推出的内部研讨教程《DeepSeek与AIGC应用》&#xff0c;以通俗易懂的方式系统解…...

测量海拔以及两点间路程(十六)

一、DigitalEarthView.cpp void CDigitalEarthView::OnCheckCheliang() {isTestJu = !isTestJu;mOSG->isTestJu(isTestJu); } 二、OSGObject.cpp void COSGObject::isTestJu(bool isTest) {theApp.bNeedModify = TRUE;while(!theApp.bCanModify)Sleep(1);if(isTest){eh-&…...

Flutter状态管理框架GetX最新版详解与实践指南

一、GetX框架概述 GetX是Flutter生态中轻量级、高性能的全能开发框架&#xff0c;集成了状态管理、路由导航、依赖注入等核心功能&#xff0c;同时提供国际化、主题切换等实用工具。其优势在于代码简洁性&#xff08;减少模板代码约70%&#xff09;和高性能&#xff08;基于观…...

8. 示例:对32位数据总线实现位宽和值域覆盖

文章目录 前言示例一&#xff1a;示例二&#xff1a;示例三&#xff1a;仿真与覆盖率分析覆盖点详细说明覆盖率提升技巧常见错误排查 示例四&#xff1a;仿真步骤 前言 针对32位数据总线实现位宽和值域的覆盖&#xff0c;并且能够用xrun运行&#xff0c;查看日志和波形。cover…...

第6篇:面向对象编程重构系统

一、OOP重构目标 数据封装:隐藏实现细节​接口抽象:规范操作入口资源自治:实现自管理生命周期扩展基础:预留多态支持接口二、完全面向对象实现(完整代码) #include <iostream> #include <Windows.h> #include <li...

CTF-web: Rust 的过程宏

Rust 的过程宏&#xff08;Procedural Macros&#xff09;是一种强大的元编程工具&#xff0c;允许你在编译时对代码进行操作和生成。与属性宏和派生宏不同&#xff0c;过程宏可以接收并处理任意 Rust 代码&#xff0c;生成新的代码片段。这里有一个简单的例子来说明 Rust 的过…...

【Kubernetes】API server 限流 之 maxinflight.go

这个文件实现了一个基于信号量(Channel)的简单限流器。 基础知识 总共有四种channel 带缓冲的channel nonMutatingChan、mutatingChan 都是带缓冲的channel &#xff0c;这类channel 的特点是&#xff1a; 这允许最多 mutatingLimit /nonMutatingLimit 个请求同时获取令牌并执…...

phpstudy安装教程dvwa靶场搭建教程

GitHub - digininja/DVWA: Damn Vulnerable Web Application (DVWA) Dvwa下载地址 Windows版phpstudy下载 - 小皮面板(phpstudy) 小皮下载地址 1选择windows 版本&#xff0c;点击立即下载 下载完成&#xff0c;进行解压&#xff0c;注意不要有中文路径 点击.exe文件进行安装…...

React核心知识及使用场景

React是一个用于构建用户界面的JavaScript库,尤其适合构建单页面应用(SPA)。它基于组件化的开发思想,主要特点是通过虚拟DOM来提高渲染效率。以下是React的核心知识和使用场景: 一. 核心知识 组件化: 类组件和函数组件:React的组件分为类组件和函数组件。类组件通过继承…...

杰发科技AC7801——滴答定时器获取时间戳

1. 滴答定时器 杰发科技7801内部有一个滴答定时器&#xff0c;该定时器是M0核自带的&#xff0c;因此可以直接用该定时器来获取时间戳。 同样&#xff0c;7803也可以使用该方式获取时间戳。 2. 滴答定时器原理 SysTick是一个24位的递减计数器&#xff0c;它从预设的重装载值…...

「爬虫实战分享:如何高效爬取某汽车官方销售排行榜」

本文目录 &#x1f496;前言一、&#x1f4ab;代理IP的作用二、&#x1f4ab;爬虫中的挑战1.代理IP的质量和稳定性2.IP封禁问题3. 反爬虫技术的升级 三、&#x1f4ab;亮数据动态代理&#xff1a;数据采集的可靠伙伴1、真实体验 四、&#x1f4ab;爬虫实战&#xff1a;使用亮数…...

AI数据分析:用DeepSeek做数据清洗

在当今数据驱动的时代&#xff0c;数据分析已成为企业和个人决策的重要工具。随着人工智能技术的快速发展&#xff0c;AI 驱动的数据分析工具正在改变我们处理和分析数据的方式。本文将着重介绍如何使用 DeepSeek 进行数据清洗。 数据清洗是数据分析的基础&#xff0c;其目的是…...

使用串口工具实现tcp与udp收发

1、使用串口工具实现tcp收发 2、使用串口工具实现udp收发...

onlyoffice 服务搭建及配置 - 前端 office 文件预览解决方案

文章目录 1. 安装1.1 环境要求1.2 安装步骤1.3 常用配置1.3.1 安装目录/config/default.json1.3.2 安装目录/config/local.json1.3.3 安装目录/nginx/conf1.3.4 配置生效 2. 网站嵌入2.1 代码示例2.2 最终效果 3. 常见问题3.1 数据库配置错误导致加载不出来 1. 安装 写文章时使…...

PostgreSQL的基本使用

参考视频&#xff1a;零基础入门PostgreSQL教程 文章目录 一、PostgreSQL是什么&#xff1f;二、基本使用1.下载2.操作 一、PostgreSQL是什么&#xff1f; PostgreSQL 是一个免费的对象-关系数据库服务器&#xff0c;在灵活的BSD许可证下发行。 二、基本使用 1.下载 2.操作 …...

【AI深度学习基础】NumPy完全指南入门篇:核心功能与工程实践(含完整代码)

NumPy系列文章 入门篇进阶篇终极篇 一、NumPy简介 NumPy&#xff08;Numerical Python&#xff09;是Python中科学计算的核心库&#xff0c;提供了高性能的多维数组对象和各种用于数组操作的函数。它是Python数据分析和科学计算的基础&#xff0c;被广泛应用于机器学习、数据…...

武汉大学生命科学学院与谱度众合(武汉)生命科技有限公司举行校企联培座谈会

2025年2月21日下午&#xff0c;武汉大学生命科学学院与谱度众合&#xff08;武汉&#xff09;生命科技有限公司&#xff08;以下简称“谱度众合”&#xff09;在学院学术厅举行校企联培专业学位研究生合作交流会。武汉大学生命科学学院副院长刘星教授、生命科学学院周宇教授、产…...

小程序画带圆角的圆形进度条

老的API <canvas id"{{canvasId}}" canvas-id"{{canvasId}}" style"opacity: 0;" class"canvas"/> startDraw() {const { canvasId } this.dataconst query this.createSelectorQuery()query.select(#${canvasId}).bounding…...

MR-图解

1、不是所有的MR都适合combine 1.1、map端统计出了不同班级的每个学生的年龄 如&#xff1a;(class1, 14)表示class1班的一个学生的年龄是14岁。 第一个map任务&#xff1a; class1 14 class1 15 class1 16 class2 10第二个map任务&#xff1a; class1 16 class2 10 class…...

[深度学习] 大模型学习2-提示词工程指北

在文章大语言模型基础知识里&#xff0c;提示词工程&#xff08;Prompt Engineering&#xff09;作为大语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;应用构建的一种方式被简要提及&#xff0c;本文将着重对该技术进行介绍。 提示词工程就是在和LLM聊…...

VSCode离线安装插件

最近在其他电脑设备上部署vscode环境出现问题&#xff0c;在vscode里直接安装插件失败&#xff0c;软件提示如下&#xff1a;&#xff08;此前已经用此方法安装过中文插件&#xff09; 这里我们选择手动下载&#xff0c;会自动在浏览器中跳转到该插件的下载链接并自动下载插件&…...

python-leetcode-删除并获得点数

740. 删除并获得点数 - 力扣&#xff08;LeetCode&#xff09; 解法 1&#xff1a;动态规划&#xff08;O(n) 时间&#xff0c;O(n) 空间&#xff09; class Solution:def deleteAndEarn(self, nums: List[int]) -> int:if not nums:return 0# 统计每个数的贡献points Cou…...

Spring Boot 流式响应豆包大模型对话能力

当Spring Boot遇见豆包大模型&#xff1a;一场流式响应的"魔法吟唱"仪式 一、前言&#xff1a;关于流式响应的奇妙比喻 想象一下你正在火锅店点单&#xff0c;如果服务员必须等所有菜品都备齐才一次性端上来&#xff0c;你可能会饿得把菜单都啃了。而流式响应就像贴…...

STM32G431RBT6——(1)芯片命名规则

相信很多新手入门STM学的芯片&#xff0c;是STM32F103C8T6&#xff0c;假如刷到个项目换个芯片类型&#xff0c;就会感到好难啊&#xff0c;看不懂&#xff0c;就无从下手&#xff0c;不知所云。其实没什么难的&#xff0c;对于一个个不同的芯片的区别&#xff0c;就像是学习包…...

React进阶之前端业务Hooks库(三)

前端业务Hooks库 hooks 方法localStorage和sessionStorager区别packages/hooks/src/useLocalStorageStatepackages/hooks/src/useSessionStorageStatepackages/hooks/src/createUseStorageState模块Hooks在不同场景下的应用Hooks陷阱前提例子useLatest和useMemoizedFn其他功能的…...

卷积神经网络梯度下降方向与参数更新方向的一致性论述

梯度下降是一种常用的优化算法&#xff0c;用于最小化损失函数&#xff0c;在机器学习和深度学习领域有着广泛的应用。分别对梯度下降、梯度方向以及参数更新采用负梯度方向的原因进行论述。 1.梯度下降 它的基本思想是通过迭代的方式来更新模型的参数&#xff0c;使得损失函数…...

python 视频网站爬虫教程,爬虫入门教程(付安装包)

文章目录 前言1. 环境准备Python安装选择Python开发环境安装必要库 2. 了解目标网站3. 发送请求获取页面内容4. 解析页面内容&#xff0c;提取视频链接5. 下载视频6. 处理反爬机制7. 完整代码示例注意事项 前言 以下为你生成一份 Python 视频网站爬虫教程&#xff0c;以爬取简…...

Is Noise Conditioning Necessary for Denoising Generative Models?论文阅读笔记

很吸引人的一个标题&#xff0c;很吸引人的一个作者&#xff0c;来读一读明神的新作&#xff0c;讲的是怎么把去噪领域的一些有意思的思想&#xff0c;特别是blind denoising和noise-level estimation的思想&#xff0c;应用到denoising diffusion模型中&#xff0c;从而去掉de…...

BIO、NIO、AIO、Netty从简单理解到使用

Java编程中BIO、NIO、AIO是三种不同的I/O&#xff08;输入/输出&#xff09;模型&#xff0c;它们代表了不同的I/O处理方式。 Netty就是基于Java的NIO&#xff08;New Input/Output&#xff09;类库编写的一个高性能、异步事件驱动的网络应用程序框架&#xff0c;用于快速开发可…...

最新版 (持续更新)docker 加速源 linux yum 源

收藏两个网站&#xff0c;配置docker 加速源与yum 源。 docker 加速源链接 Docker/DockerHub 国内镜像源/加速列表&#xff08;2月25日更新-长期维护&#xff09;-腾讯云开发者社区-腾讯云https://cloud.tencent.com/developer/article/2485043 yum 源 CentOS7停服后yum源配置…...

MapReduce编程模型

MapReduce编程模型 理解MapReduce编程模型独立完成一个MapReduce程序并运行成功了解MapReduce工程流程掌握并描述出shuffle全过程&#xff08;面试&#xff09;独立编写课堂及作业中的MR程序理解并解决数据倾斜 1. MapReduce编程模型 Hadoop架构图 Hadoop由HDFS分布式存储、M…...