前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >今天你学C++了吗?——string(上)

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

作者头像
用户11352420
发布2025-03-02 22:12:57
发布2025-03-02 22:12:57
9800
代码可运行
举报
文章被收录于专栏:编程学习编程学习
运行总次数:0
代码可运行
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的使用~

代码语言:javascript
代码运行次数:0
运行
复制
#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做返回值!!!

例:

代码语言:javascript
代码运行次数:0
运行
复制
#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的使用例子

代码语言:javascript
代码运行次数:0
运行
复制
#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;
	//使用范围for
	for (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/char
	cout << "使用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

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

构造

查阅文档:

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

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

代码语言:javascript
代码运行次数:0
运行
复制
#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版本~使用的时候会匹配最合适的~

代码语言:javascript
代码运行次数:0
运行
复制
#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;//实现修改
	}
	//范围for
	for (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 迭代器——可读不可写~

例:

代码语言:javascript
代码运行次数:0
运行
复制
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_iterator
	while (it2 != s1.cend())
	{
		//*it2 += 1; //const_iterator不可以修改
		cout << *it2 << " ";
		++it2;
	}
	cout << endl;

}

画图理解:

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

代码语言:javascript
代码运行次数:0
运行
复制
//反向迭代器
string::reverse_iterator rit = s1.rbegin();
while (rit != s1.rend())
{
	*rit += 1;
	cout << *rit << " ";
	++rit;
}

画图理解:

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

范围for

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

代码语言:javascript
代码运行次数:0
运行
复制
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()
    • 调整字符串的长度到指定的新大小。
    • 如果新大小小于当前长度,则字符串会被截断~
    • 如果新大小大于当前长度,则会在字符串末尾添加指定数量的字符(默认为空字符,但实际表现可能不显式显示这些空字符,且依赖于实现可能会用空格或其他字符填充)
    • 不直接改变字符串的底层存储容量(尽管如果新大小远大于当前容量,内部可能会重新分配内存)

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

代码语言:javascript
代码运行次数:0
运行
复制
#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小于或等于当前容量,则不做任何操作~(具体平台具体分析)
    • 这可以用于优化性能,通过减少内存重新分配的次数(减少扩容,提高效率)
代码语言:javascript
代码运行次数:0
运行
复制
#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()
    • 调整字符串的长度到指定的新大小。
    • 如果新大小小于当前长度,则字符串会被截断~
    • 如果新大小大于当前长度,则会在字符串末尾添加指定数量的字符(默认为空字符,但实际表现可能不显式显示这些空字符,且依赖于实现可能会用空格或其他字符填充)
    • 不直接改变字符串的底层存储容量(尽管如果新大小远大于当前容量,内部可能会重新分配内存)

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

代码语言:javascript
代码运行次数:0
运行
复制
#include <iostream>
#include <string>
using namespace std;
int main()
{
	//vs2022 测试string类的resize
	string 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[]则可能导致未定义行为(例如访问越界内存),会直接断言报错~

例:

代码语言:javascript
代码运行次数:0
运行
复制
#include <iostream>
#include <string>
#include <stdexcept> // for std::out_of_range

int 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'不是有效字符~

代码语言:javascript
代码运行次数: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

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

例:

代码语言:javascript
代码运行次数:0
运行
复制
void test1()
{
	string s1("HHHHHH");
	cout << s1 << endl;
	//尾插一个字符
	s1.push_back('H');
	cout << s1 << endl; 
	//尾删一个字符
	s1.pop_back();
	cout << s1 << endl;
}
append

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

我们来看看使用:

代码语言:javascript
代码运行次数:0
运行
复制
//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个字符G
	s1.append(2, 'G');
	cout << s1 << endl;
}

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

代码语言:javascript
代码运行次数:0
运行
复制
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+=

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

代码语言:javascript
代码运行次数:0
运行
复制
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的接口也是十分丰富的,我们一下子肯定也记不住,最好的办法是边学边记,在实践中学会查文档,加深理解记忆~

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

代码语言:javascript
代码运行次数:0
运行
复制
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的接口就比较少,我们来简单使用一下~

代码语言:javascript
代码运行次数:0
运行
复制
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类似于赋值操作,但是它的接口多样,可以满足我们的不同需求,我们来看看它们的接口~

代码语言:javascript
代码运行次数:0
运行
复制
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也就是可以进行替换,我们来看看它丰富的接口~

代码语言:javascript
代码运行次数:0
运行
复制
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个字符替换成s1
	s3.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也就是交换字符串~在后面底层实现的时候我们会更加清楚~

代码语言:javascript
代码运行次数:0
运行
复制
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风格字符串进行比较。根据比较结果返回负值、零或正值,分别表示小于、等于或大于。

测试:

代码语言:javascript
代码运行次数:0
运行
复制
void test1()
{
	string s1("AAAAAA");
	string s2("AAAAAA");
	if (s1.c_str() == s2.c_str())
		cout << "true" << endl;
	else
		cout << "false" << endl;

	if (s1.data() == s2.data())
		cout << "true" << endl;
	else
		cout << "false" << endl;
}
代码语言:javascript
代码运行次数:0
运行
复制
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的使用

代码语言:javascript
代码运行次数:0
运行
复制
void test3() 
{
	//copy从字符串复制字符序列
	char buffer[20];
	std::string str("Test string...");
	//从位置为5的字符开始复制8个字符到buffer
	std::size_t length = str.copy(buffer, 8, 5);
	buffer[length] = '\0';
	std::cout << "buffer contains: " << buffer << '\n';

	//substr生成子字符串
	string s1 = "Hello Xiaodu!";
	//返回从指定位置开始、指定长度的子字符串
	//从位置为6的字符开始复制7个字符给s2
	string s2 = s1.substr(6, 7);
	cout << s1 << endl;
	cout << s2 << endl;
}

运算符重载

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

relational operators (string)

使用:

代码语言:javascript
代码运行次数:0
运行
复制
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 + / << / >>

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

代码语言:javascript
代码运行次数:0
运行
复制
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.

代码语言:javascript
代码运行次数:0
运行
复制
void test6()
{
	// 创建一个string对象来存储输入的行
	string inLine;

	// 提示用户输入一行文本
	cout << "请输入一行文本(按Enter结束输入): ";

	// 使用getline从标准输入读取一行文本,直到遇到换行符
	getline(cin, inLine);

	// 打印出用户输入的文本
	cout << "您输入的文本是: " << inLine << endl;
}

2.

代码语言:javascript
代码运行次数:0
运行
复制
void test7()
{
	// 创建一个string对象来存储输入的行
	string inLine;

	// 提示用户输入一行文本
	cout << "请输入一行文本(按#结束输入): ";

	// 使用getline从标准输入读取一行文本,直到我们选择的结束符
	getline(cin, inLine, '#');

	// 打印出用户输入的文本
	cout << "您输入的文本是: " << inLine << endl;
}

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-03-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • auto关键字
  • 范围for
  • string类
    • 构造
    • 数据遍历
      • 下标+[ ]
      • 迭代器(通用的容器访问方式)
      • 范围for
    • Capacity
    • 数据访问
    • 数据修改
      • push_back和pop_back
      • append
      • operator+=
      • insert
      • erase
      • assign
      • replace
      • swap
    • 数据查找
    • 运算符重载
      • relational operators (string)
      • operator + / << / >>
      • getline
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档