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

C++ --- string

C++ --- string

  • 简介
  • 1、构造函数
  • 2、迭代器(主流的遍历方式)
    • 2.1字符串经典遍历和修改的方式
    • 2.2使用迭代器遍历和修改字符串
    • 2.3使用范围for遍历对象(C++11支持的新特性)
  • 3、常见,常用方法或重载
    • 3.1查询大小和容量管理
    • 3.2增
    • 3.3删
    • 3.4改
    • 3.5查
    • 3.5其他

简介

在C++中,std::string 是标准模板库(STL)提供的字符串类,用于表示和操作字符序列。它是C风格字符串(char*char[])的现代化替代品,具有更安全、更便捷的特性。
不过string类面世比STL早,当中很多定义的功能对比真正的STL中提供的其他类来说即繁杂又重复,不过其思想是符合STL的。

STL的六大组件:
在这里插入图片描述

学前必知:
通过string类生成的对象的成员变量有三个,一个是指向底层数组的指针,一个siz字符串的大小,一个capacity字符串的容量。
数组内存储的字符串和C语言是一样的,末尾都会有一个\0的存在。

1、构造函数

(1)默认构造

	//默认的:(1)	string();string s1;                //创建一个空字符s1,后续可以手动初始化s1 = "abcdefg";string s2 = "abcdefg";    //上述两步合并 

之所以能写成赋值的这种形式,是因为string类重载了赋值符号即operator = ,所以才会出现赋值形式的构造。

(2)拷贝构造

	//拷贝:(2)	string(const string & str);string s4("abc");string s5(s4);//string s5 = s4;         //此写法也可

(3)部分拷贝 — 截取字符串

	//截取字符串:(3)	string(const string & str, size_t pos, size_t len = npos);//pos --- 截取的起始位置//npos --- 截取的长度string s6(s1, 1, 5);      //输出bcdef//string s6(s1, -1, 5);     //程序直接崩溃string s7(s1, 1, 10);     //输出bcdefg

监视窗口观察:
在这里插入图片描述

1)当截取的起始位置不是一个正确的值时,此时运行程序,程序会直接崩溃。
2)当截取的长度大于字符串本身的长度时,默认输出从截取位置之后的整个字符串,并不会报错。

(4)拷贝来自C语言方式的字符串(字符数组)

	//拷贝来自C语言方式的字符串:(4)	string(const char* s);char ch2[5] = "abc";string s8(ch2);           //这里ch2就已经是地址了cout << s8 << endl;string s3("abcdefg");     //这样才是常写的形式

(5)部分拷贝来自C语言方式的字符串(字符数组)

	//部分拷贝来自C语言方式的字符串:(5)	string (const char* s, size_t n);char ch3[10] = "abcdefgh";string s9(ch3,5);cout << s9 << endl;

监视窗口观察:
在这里插入图片描述

(6)使用n个c字符填充整个字符串

	//使字符串充满n个C字符:(6)	string (size_t n, char c);string s10(10, 'n');

监视窗口观察:
在这里插入图片描述

上述构造方法中(1),(2),(4)用得较多,其他几个用得少。

2、迭代器(主流的遍历方式)

迭代器是STL中一个新的概念,虽说概念较新,但是其功能就是遍历对象中的元素。
STL中的容器都有自己的迭代器,但是基础使用上都差不多,所以知识可迁移性很强。
这里只介绍它的使用,不介绍其底层机理。

迭代器是嵌套在string类中的类类型,使用方式就是容器名称 : : iterator 迭代器名称 = 对象.begin()或者对象.end(),string类中就是string : : iterator 迭代器名称 = 对象.begin()或者对象.end(),迭代器名称可以随便取,但是通常方便记忆就取了it,iterator的前两个字母。
想要获取迭代器指向的内容,语法就和指针相似,解引用迭代器名称即可。

上面说到迭代器的功能是遍历对象中的元素,回到string类这里,我们之前就已经学习过一种遍历字符串的方式,一个for循环使用[ ]加下标来遍历。

2.1字符串经典遍历和修改的方式

示例代码如下:

	//string类的经典遍历方式:[ ]加下标string s2("hello world");for (int i = 0; i < s2.size(); i++){cout << ++s2[i] << " ";}cout<<endl;

运行结果:
在这里插入图片描述
为什么s2对象能使用数组的形式使用[ ]来确定元素?
是因为string类中将[ ]给重载了:
在这里插入图片描述
传递一个pos位置,其返回此位置上的元素值的引用。
s2.size(),是string类中定义的之中确定字符串长度的方法,在此类中还有一个length(),也是确定字符串长度的方法(此方法用的少)。

2.2使用迭代器遍历和修改字符串

示例代码如下:

	//2、迭代器//获取正向迭代器//begin() --- 获取起始位置的迭代器//end() --- 获取最后一个位置的下一个位置的迭代器//其区间是左闭右开区间string s1("hello world");string::iterator it1 = s1.begin();string::iterator it2 = s1.end();//for形式for (it1 = s1.begin(); it1 != it2; ++it1) {//++(*it1);cout << *it1 << " ";       //打印其指向的内容,形式上就是指针的模样}cout << endl;//while形式while (it1 != it2){cout << *it1 << " ";++it1;}cout << endl;

运行结果如下:
在这里插入图片描述
这里的begin是返回对象首元素位置的迭代器,end是返回首元素最后一个元素的下一个位置的迭代器,其区间表示为:[begin,end),是一个左闭右开区间,在C/C++语言内凡是存在区间的基本上都是左闭右开区间。

既然有正向迭代器也就有反向迭代器:

	//获取反向迭代器//rbegin() --- 获取最后一个位置的迭代器//rend() --- 获取起始位置的前一个位置的迭代器//其区间是左开右闭区间string s2("hello world");string::reverse_iterator r_it1 = s2.rbegin(); string::reverse_iterator r_it2 = s2.rend();for (r_it1; r_it1 != r_it2; r_it1++){cout << *r_it1;}cout << endl;

运行结果:
在这里插入图片描述
这里的rbegin是获取最后一个位置的迭代器,rned是获取起始位置的前一个位置的迭代器,对其++是从最后一个元素位置向前遍历,和正向迭代器是相反的操作。

也有对于const修饰的对象的常量迭代器:

	//获取常量迭代器//cbegin() --- 获取起始位置的迭代器//cend() --- 获取最后一个位置的下一个位置的迭代器//区间同样是左闭右开区间const string s3("hello world");string::const_iterator c_it1 = s3.cbegin();string::const_iterator c_it2 = s3.cend();for (c_it1; c_it1 != c_it2; c_it1++){cout << *c_it1;}cout << endl;

常量反向迭代器:

	//获取常量反向迭代器const string s4("hello world");string::const_reverse_iterator c_r_it1 = s3.crbegin();string::const_reverse_iterator c_r_it2 = s3.crend();

上述迭代器的用法只是众多迭代器使用方式的一种,之后其他的使用方式在后面的容器里再学习。

2.3使用范围for遍历对象(C++11支持的新特性)

在讲解范围for之前要先了解auto,这是一个自动识别类型的关键字。
示例代码如下:

	// auto关键字 --- 自动识别类型auto x = 10;    // 这里auto就识别成int类型auto y = 3.14;  // 这里auto就识别成double类型int& m = x;// auto z = m;   // 这里的z是对于引用m的值的赋值,不是m的引用auto& z = m;     // 正确写法m++;             // 是引用则z,m ,x都会改变// 指针 --- 下面两种写法都可以auto px1 = &x;   //灵活性更高,当 & 删除时,则识别为int类型auto* px2 = &x;  //较为固定,只能识别指针类型

调试窗口观察:
在这里插入图片描述

范围for示例代码:

	// 范围for --- 支持各种STL容器// 语法auto 对象名 :需要遍历的对象名 // 其底层就是迭代器// 是将 *迭代器对象拷贝给auto对象// 并且自动判断(!=),自动迭代(++)string s4("hello world");//这种写法是只能遍历//当然也能直接指定类型://for(char e : s4)for (auto e : s4){cout << e << " ";}cout << endl;//这种写法才能修改for (auto& e : s4){cout << ++e << " ";}cout << endl;//特殊用法,数组也能使用范围for遍历char arr[10] = "abcdefghi";for (auto e : arr){cout << e << " ";}cout << endl;

总结:对于string类,for循环加 [ ] 加下标遍历方式更加常用,不过对于其他STL容器,迭代器是更加主流的遍历方式,不过上述三种的遍历方式都是灵活的去使用,并不是死板的。

3、常见,常用方法或重载

3.1查询大小和容量管理

(1)size(),length()
功能:size()和length()都是string类查询对象大小的方法,不过更多使用的是size(),因为length()先出现,而为了规范性,和其他容器的查询大小方法一致,后续就添加了size()。

示例代码如下:

	// 1.查询对象大小// size() , length() ,功能上相同string s1("hello world!!");// 结果一致 --- 13cout << s1.size() << endl;cout << s1.length() << endl;

常用于for循环的结束条件。

(2)capacity()
功能:capacity()是一个查看对象容量大小的一个接口。

示例代码如下:

	//(2)capacity()// 查询对象的容量string s2("hello world");string s3;cout << s2.capacity();                 //起始是15cout << s3.capacity() << endl;         //即使是空对象,capacity也是初始为15// 查看扩容机制 --- 几倍扩容 --- 除了第一次为两倍,后续均为1.5倍扩容string s4;size_t cy = s4.capacity();cout << cy << endl;for (int i = 0; i < 100; i++){s4.push_back('L');if (s4.capacity() != cy){cy = s4.capacity();cout << cy << endl;}}

查看扩容机制的运行结果:
在这里插入图片描述
15到31,两倍左右;31到47,约1.5倍;47到70,约1.5倍;后续一样。

(3)reserve()
功能:将字符串容量调整为计划的大小,长度最多为n个字符

示例代码如下:

	// 请求将字符串容量调整为计划的大小,长度最多为n个字符。string s5("hello world");          //capacity = 15// 用法一:扩容(最常用的)s5.reserve(100);cout << s5.capacity() << endl;     //capacity = 111// 用法二:缩容 --- 这是一个不具约束力的请求,也就是说可能会缩容,或者不缩容s5.reserve(30);                 cout << s5.capacity() << endl;     //capacity = 111// 此接口的n值不会影响字符串的长度s5.reserve(3);cout << s5.capacity() << endl;     //capacity = 111

最常用的用法是提前给定容量大小,减少反复扩容的操作,提高效率,本质上是一个空间换时间的操作。

优化上述capacity中扩容机制的代码:

	string s4;s4.reserve(100);           //这样一来直接开到111容量,可以减少后续的扩容操作size_t cy = s4.capacity();cout << cy << endl;for (int i = 0; i < 100; i++){s4.push_back('L');if (s4.capacity() != cy){cy = s4.capacity();cout << cy << endl;}}

(4)shrink_to_fit()
功能:这是一个纯粹的缩容接口,将对象的capacity缩容到size大小缩容在string类里面是一个不具有约束里的请求。

示例代码如下:

	// 这是一个纯粹的缩容接口string s6;s6.reserve(100);cout << "缩容之前:" << s6.capacity() << endl;      s6.shrink_to_fit();cout << "缩容之后:" << s6.capacity() << endl;

运行结果:
在这里插入图片描述

缩容机制(图解):
在这里插入图片描述
基于上述机制,缩容的代价是很大的,要少用此接口,故缩容操作是一个不具约束力的请求。

(5)resize()
功能:更改size或者capacity的大小

示例代码如下:

	// void resize(size_t n);               //此重载改变size大小,若大于size或者capacity,则字符串后续会被填充进空字符(\0)// void resize(size_t n, char c);       //此重载改变size大小,若大于size或者capacity,则字符串后续会被填充进给定的c字符// 1、当 n > capacity(大于容量了故也大于size) --- 将size设定到目标大小并且进行扩容操作string s7("hello world");cout << "resize之前的size:" << s7.size() << endl;            // 11cout << "resize之前的capacity:" << s7.capacity() << endl;    // 15s7.resize(30,'x');s7.resize(30);              //此写法就是将x替换成了\0,这一个看不见的字符cout << s7 << endl;cout << "resize之后的size:" << s7.size() << endl;            // 30cout << "resize之后的capacity:" << s7.capacity() << endl;    // 31// 2、当 capacity > n > size  --- 和一一样的功能// 3、当 n < size  --- 只保留给定的n个字符,多的字符全部删除string s8("hello world");cout << "resize之前的size:" << s8.size() << endl;            // 11cout << "resize之前的capacity:" << s8.capacity() << endl;    // 15s8.resize(5);cout << s8 << endl;          //hellocout << "resize之后的size:" << s8.size() << endl;            // 5cout << "resize之后的capacity:" << s8.capacity() << endl;    // 15

运行结果:
在这里插入图片描述

3.2增

(1)operator += - - - 用得最多的
功能:可以添加字符串,字符或者string类的对象。

示例代码如下:

	// 2.增// 增加一个string类对象(1):    string& operator+= (const string & str);// 增加一个字符串(2):          string& operator+= (const char* s);// 增加一个字符(3):            string& operator+= (char c);string s2;string s3("2025/5/19 ");s2 += s3;                 //(1)的用法s2 += "hello";            //(2)的用法s2 += "_";                //(2)的用法s2 += '\n';               //(3)的用法s2 += "world";            //(2)的用法cout << s2 << endl;

运行结果:
在这里插入图片描述

(2)operator +
功能:连接两个string类对象,并返回一个新串

示例代码如下:

	// 连接两个string类对象,并返回一个新串string s5("aaa");string s6("bbb");cout << "s5 + s6:" << s5 + s6 << endl;

运行结果:
在这里插入图片描述

(3)push_back()
功能:可以将单个字符添加到字符串末尾

示例代码如下:

	//(2)push_back()// 将单个字符添加到字符串末尾string s4("hello");s4.push_back(' ');s4.push_back('w');s4.push_back('o');s4.push_back('r');s4.push_back('l');s4.push_back('d');cout << s4 << endl;

运行结果:
在这里插入图片描述

(4)insert()
功能:在指定位置插入字符或者字符串

示例代码如下:

	// 任意位置插入元素// 插入一个string对象(1):        string& insert(size_t pos, const string & str);// 插入一个子串(2):              string& insert(size_t pos, const string & str, size_t subpos, size_t sublen);// 插入一个C风格的字符串(3):     string & insert(size_t pos, const char* s);// 插入一个字符串的前n个字符(4): string& insert(size_t pos, const char* s, size_t n);// 插入n个c字符(5):              string& insert(size_t pos, size_t n, char c);string s7("abgh");string s8("1234");cout << s7.insert(2, s8) << endl;            // (1) --- ab1234ghcout << s7.insert(2, s8, 2, 4) << endl;      // (2) --- ab34ghcout << s7.insert(2, "cdef") << endl;        // (3) --- abcdefghcout << s7.insert(2, "xyz", 2) << endl;      // (4) --- abxyghcout << s7.insert(2, 5, 'x') << endl;        // (5) --- abxxxxxgh

总结:
insert最常用的重载是(1),(3),(5),并且要谨慎使用(不是不安全),而是insert底层涉及数据挪动。

(5)append - - - 用的少这里只介绍最实用的几个重载
功能:可以将字符或者字符串添加到字符串末尾

示例代码如下:

	// 可以将字符或者字符串添加到字符串末尾//插入一个string对象(1):      string& append(const string & str);//插入一个字符串(2):          string & append(const char* s);//插入n个字符(3):             string& append(size_t n, char c);string s9;string s10("hello");s9.append(s10);             // (1) --- hellos9.append(" world");        // (2) --- hello worlds9.append(3, '!');          // (3) --- hello world!!!

3.3删

(1)erase()
功能:删除指定位置之后的n个字符,也需谨慎使用,底层同样涉及数据挪动,效率低下。

示例代码如下:

	// 删除指定位置之后的n个字符string s1("aaabbbcccddd");s1.erase(3, 6);     // aaaddd// 也可以不指定第二个参数len ,这样就是使用的缺省值npos,而npos是无符号整数的最大值,用了-1表示// 这样的用法就相当于自第三个位置之后所有的数据全部都删除s1.erase(3);        // aaa

(2)pop_back()
功能:删除字符串末尾的一个字符

示例代码如下:

	// 删除字符串末尾的一个字符string s2("abcde?");s2.pop_back();              // abcdes2.pop_back();              // abcds2.pop_back();              // abc

(3)clear()
功能:清空整个字符串

示例代码如下:

	// 清空整个字符串string s3("hello shsh>>?");s3.clear();                 // 变为空值: ""

3.4改

replace()
功能:修改从pos位置开始,长度为n的字符或者字符串,它的效率也是很低的,谨慎使用,原因基本一致(erase和insert)

示例代码如下:

	// 4.改 --- 或者替换// (1)replace// 从pos位置开始,插入n个长度的string类对象(1):    string& replace(size_t pos, size_t len, const string & str);// string类对象的部分(2):    string& replace(size_t pos, size_t len, const string & str,size_t subpos, size_t sublen);// subpos --- pos位置开始// sublen --- 修改的长度// C语言风格字符串(3):    string & replace(size_t pos, size_t len, const char* s);// C语言风格字符串的一部分(4):    string& replace(size_t pos, size_t len, const char* s, size_t n);// 使用n个字符替换指定目标字符(串)(5):    string& replace(size_t pos, size_t len, size_t n, char c);string s1("aaabbbccceee");string s2("ddd");cout << s1.replace(9, 3, s2) << endl;          //(1) --- aaabbbcccdddcout << s1.replace(9, 3, s2, 0, 2) << endl;    //(2) --- aaabbbcccddcout << s1.replace(9, 3, "eee") << endl;       //(3) --- aaabbbccceeecout << s1.replace(9, 3, "eee", 1) << endl;    //(4) --- aaabbbcccecout << s1.replace(9, 3, 3, 'n') << endl;      //(5) --- aaabbbcccnnn

其中常用的为(3)

3.5查

(1)empty
功能:检查字符串是否为空,若为空,则返回0(false),反之返回1(true)

示例代码如下:

	//(1)empty()// 查看(判断)字符串是否为空,若不为空则返回0(false),反之返回1(true)string s4("hello shsh>>?");bool b1 = s4.empty();s4.clear();bool b2 = s4.empty();cout << b1 << endl;          //0cout << b2 << endl;          //1

(2)operator[ ]
功能:返回第pos位置上的元素,并且该重载会检查数组越界,若出现数组越界操作,则会直接程序崩溃。

示例代码如下:

	//(2)operator[] // 返回第pos位置的数据string s2("hello world!!");cout << s2[0] << endl;       //hcout << s2[0]++ << endl;     //i --- 也可以进行修改操作cout << s2[7] << endl;       //ocout << s2[12] << endl;      //!cout << s2 << endl;          //iello world!!// 该重载会检查数组越界 --- 直接会发生程序崩溃//s2[14];// 普通数组不会程序崩溃,因为对于C/C++编译器来说检查普通数组的越界操作是一个影响效率的操作int arr[10] = { 0 };arr[20];

(3)at()
功能:功能和operator[ ]一样,不过处理错误时是抛异常,而非检查返回值

示例代码如下:

	//(3)at()// 功能和operator[ ]一样,不过处理错误时是抛异常,而非检查返回值string s3("123456");s3.at(0)++;cout << s3 << endl;         //23456cout << s3.at(5) << endl;   //6try{//at接口的越界操作cout << s3.at(10) << endl;  //抛异常}catch(exception& e){cout << e.what();}

运行结果:
在这里插入图片描述
invalid string position此异常即为字符串位置无效。

(4)front()与back()
功能:返回字符串的起始位置、末尾位置的元素,这两操作都不能对空对象进行操作,会直接导致程序崩溃

示例代码如下:

	//(4)front()// 返回字符串的起始字符,若对于空对象进行此操作会导致程序崩溃string s4("12345");cout << s4.front() << endl;         //1//s4.clear();        //对于空对象进行此操作会导致程序崩溃//cout << s4.front();//(5)back()// 返回字符串的末尾字符,同理进行对空对象的操作会导致程序崩溃string s5("12345");cout << s5.back() << endl;          //5//s5.clear();        //不能对空对象进行此操作//cout << s5.back();

(5)find()和rfind()
功能:find()是查询指定的字符返回其下标、或者查询字符串返回起始位置的下标。rfind()是反向查询字符或者字符串,并返回其下标。

示例代码如下:

	// 查询字符串:	size_t find(const char* s, size_t pos = 0) const;// 查询字符: 	size_t find(char c, size_t pos = 0) const;// 第二个参数pos就是从串的哪一个位置开始查询,没有指定则使用缺省值0,即从头开始查询// 若没有找到指定的字符或者字符串,则返回重载的全局静态成员常量npos,这是一个非常大的数// 返回指定字符或者字符串(起始位置)的下标string s6("hello world!!");size_t pos1 = s6.find('h');         // 这里返回 h 的下标:0size_t pos2 = s6.find("!!");        // 这里返回第一个!号的下标:11size_t pos3 = s6.find('h',1);       // 这里返回npos,即非常大的数cout << pos1 << endl;cout << pos2 << endl;cout << pos3 << endl;

运行结果:
在这里插入图片描述

(6)substr()
功能:提取子串,从pos位置开始,提取len个字符,其区间是 [ ) 式。

示例代码如下:

	// string substr (size_t pos = 0, size_t len = npos) const;// pos --- 指定从哪个位置开始查找,若不指定则使用缺省值0,即从头开始查找// len --- 指定的提取子串的长度,若不指定则使用缺省值npos,即提取直到字符串末尾// 返回值是一个string类型// 若给定的 pos 非法(pos > size()),则会抛出异常 --- 超出范围异常// // 查询子串string s7("123abc!!");string str1 = s7.substr();           // 都不指定则提取整个字符串string str2 = s7.substr(3);          // --- abc!!string str3 = s7.substr(0, 3);       // --- 123string str4 = s7.substr(6, 2);       // --- !!cout << "str1 = " << str1 << endl;cout << "str2 = " << str2 << endl;cout << "str3 = " << str3 << endl;cout << "str4 = " << str4 << endl;try{string str5 = s7.substr(10);     //这里 pos > s7.size()}catch(const exception& e){cout << e.what() << endl;}

运行结果:
在这里插入图片描述

3.5其他

(1)c_str()和data()
功能:返回底层指向的数组的指针

示例代码如下:

	// 返回底层指向数组的指针// 返回值都是const char*string s1("hello world");const char* pstr1 = s1.c_str();const char* pstr2 = s1.data();

在某些使用C语言的函数的情况下会使用。

(2)重载全局的各种关系运算符
==、!=、<、<=、>、>=
在这里插入图片描述
其比较方式和C语言的strcmp函数相同,依次比较对应位置上的字符的ASCII值
返回值均是bool类型

相关文章:

  • 全局异常处理器
  • 开篇:MCP理论理解和学习
  • 基于Python的自动化视频编辑脚本设计,能够处理视频剪辑、添加字幕、文本动画、音效和图形等功能
  • 24. 日志的基本实现方式
  • 第十天的尝试
  • Gateway全局过滤器:接口耗时统计与黑白名单配置
  • Linux环境变量与地址空间
  • maxkey单点登录系统
  • LeetCode-贪心-买卖股票的最佳时机
  • SOC-ESP32S3部分:11-任务创建
  • 基于亚博K210开发板——lvgl 图形化实验
  • ubuntu ollama /Dify/Docker部署大模型
  • 刷题 | 牛客 - js中等题-下(更ing)30/54知识点解答
  • 多态的总结
  • 【C语言】习题练手套餐 2
  • 在WPF程序中设置背景图片
  • 深度解析NL2SQL:从语义理解到工程实践的全链路探索
  • 向量数据库Milvus03-高级功能与性能调优
  • 探索产品经理的MVP:从概念到实践
  • AVL树简介与部分实现
  • 学校网站建设的优势和不足/北京seo网站优化培训
  • 公司做网站费用会计处理/石家庄网站建设培训
  • 哪些网站教你做美食的/网络营销方式有几种
  • 网站分类目录大多数依靠的是人为提交而不是蜘蛛/页面优化
  • 运城市住房和城乡建设厅网站/搜索引擎排名优化是什么意思
  • 用php做网站需要什么/有产品怎么找销售渠道