string【1】介绍与使用(超详解哦)

12 篇文章 0 订阅

引言

在C语言部分,我们对于字符串的操作经常借助C标准库提供的一系列操作字符串的库函数(strlen等)来实现的。
但是,这样去操作字符串,函数与其操作的字符串是分离的,不符合面向对象的思想,使用时较为麻烦;而且需要自己管理底层的空间,容易导致越界访问的问题。

在C++标准库中提供了管理字符串的string类,封装了方法与元素使操作字符串时更加方便:
在这里插入图片描述

  1. strings是表示字符序列的对象
  2. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traitsallocator作为basic_string的默认参数
  3. ……(后面会详细介绍)

使用string类时,必须包含#include<string>头文件以及using namespace std;
string文档详解

string类常用接口

string类的元素其实与之前实现的顺序表类似,包括一个指针指向存储数据的空间(_date)、另两个数字表示元素个数(_size)与容量(_capacity

构造函数

在这里插入图片描述
string的构造方式有许多,包括无参构造、拷贝构造、C字符串构造、字符串中部分构造等:
在这里插入图片描述

例如:

//测试各种string的构造重载

int main()
{
	//无参构造s1
	string s1;
	cout << "1: " << s1 << endl;
	//使用C字符串(字符串常量)构造s2
	string s2("hello qqq");
	cout << "2: " << s2 << endl;
	//使用string对象s2拷贝构造s3
	string s3(s2);
	cout << "3: " << s3 << endl;
	//使用s2从下标为6的位置后的字符构造s4
	string s4(s2, 6);
	cout << "4: " << s4 << endl;
	//使用常量字符串的前5个字符构造s5
	string s5("abcdef", 5);
	cout << "5: " << s5 << endl;
	//使用6个字符'q'构造s6
	string s6(6, 'q');
	cout << "6: " << s6 << endl;
	//使用s6首尾位置的迭代器构造s7
	string s7(s6.begin(), s6.end());
	cout << "7: " << s7 << endl;
	return 0;
}

在这里插入图片描述
接下来就逐一说明:

  • 在这里插入图片描述
    无参构造了一个string对象s1,所以打印s1时没有显示内容;
    在前面的内容中已经对类和对象有了一定的了解,使用cout << s1 << endl;打印类对象实际是 <<运算符重载

  • 在这里插入图片描述
    使用常量字符串"hello qqq"构造对象s2

  • 在这里插入图片描述
    拷贝构造,使用类对象s2拷贝构造s3

  • 在这里插入图片描述
    在这里插入图片描述
    使用类对象s2从下标为6的位置以后的元素构造s4,第三个参数使用缺省参数;
    这个构造函数的重载形式是有三个参数:const string& strsize_t possize_t len = npos,表示用str类中从pos位置开始的len个字符构造一个新类对象。len是一个缺省参数,缺省值为npos,string中定义npos的值为-1,放在一个无符号数len中就将是一个很大的数,即表示用从pos位置以后的所有元素构造

  • 在这里插入图片描述
    使用常量字符串"abcdef"的前5个字符构造s5, 即"abcde"

  • 在这里插入图片描述
    使用6个字符'q'构造string对象s6

  • 在这里插入图片描述
    在这里插入图片描述
    使用迭代器区间,从s6.begin()s6.end()来构造s7这个区间是左闭右开的
    迭代器iterator,提供了一种统一的方式来访问集合中的元素,而不需要了解底层集合的具体实现细节,使用时类似于指针(string中就是指针)。 这里的构造重载是一种模板,这意味着不仅可以使用string的迭代器区间来构造string对象。

容量操作

对于容量的操作,就是对成员变量_size_capacity的操作,这里只介绍常用的几种
在这里插入图片描述

size与length

在这里插入图片描述
在这里插入图片描述
size()length()的作用都是求string对象中的数据长度,他们的返回值相同,但是由于历史版本原因存在两个成员函数。

int main()
{
	string s1("abcdefg");
	cout << s1.size() << endl;
	return 0;
}

在这里插入图片描述

需要注意的是:string对象与C字符串不同,C字符串以'\0'为结束标志,所以在使用strlen计算字符串长度时是以'\0'为结束标志的;而对于string,没有结束标志,它的长度其中存储的字符的个数,也就是其属性_size的值
例如当string对象存储的字符序列中存在'\0'

int main()
{
	string s1("abcdefg");
	s1 += '\0';
	s1 += "666666";
	cout << s1.size() << endl;
}

在这里插入图片描述

可以通过resize()函数来改变string中元素个数

capacity

capacity()用于查看string对象的容量
在这里插入图片描述

int main()
{
	string s1("abcdefg");
	cout << s1.capacity() << endl;
	return 0;
}

在这里插入图片描述

capacity的值会大于等于size的值,容量大于等于元素个数,这不难理解。

当我们修改string对象中的字符序列时,可能会出现修改后的size大于capacity的情况,这时就会发生扩容。扩容一般为一倍或一点五倍扩(不同环境下不同)。但是容量大小不会大于size的理论最大值,这个值可以通过max_size函数查到,不同环境下的值会有不同。

vs2019下扩容演示(1.5倍):

int main()
{
	string s1;
	size_t old = 0;
	while (s1.capacity() <= 128)
	{
		s1 += '6';
		if (old != s1.capacity())
		{
			cout << s1.capacity() << endl;
			old = s1.capacity();
		}
	}
	return 0;
}

在这里插入图片描述

resize

resize用于修改string对象字符序列的元素个数,即_size的值
在这里插入图片描述
resize函数有两个重载版本,可以将string对象的字符个数改为n
n小于原string对象的_size时,将保留原对象的前n个字符,_size的值为n
n大于原string对象的_size时,将保留原字符,超出原_size的部分使用传参指定的字符c补足,若未传参,使用'\0'补足。

int main()
{
	//使用常量字符串初始化string对象s1
	string s1("abcde");
	cout << s1 << " " << s1.size() << endl;
	//将s1元素个数删到3
	s1.resize(3);
	cout << s1 << " " << s1.size() << endl;
	//使用字符!将s1元素个数增加到10
	s1.resize(10, '!');
	cout << s1 << " " << s1.size() << endl;
	return 0;
}

在这里插入图片描述

reserve

reserve用于修改string对象的容量,即_capacity的值
在这里插入图片描述
reserve接收一个size_t型的参数n,表示将string对象的容量扩展至n个字符(或大于n个字符),函数并不会改变string字符序列的长度(元素个数)。

(当n的值小于原对象的容量时,函数行为取决于容器实现,但最后的容量一定大于n的值)

int main()
{
	//使用常量字符串初始化string对象s1
	string s1("abcde");
	cout << s1 << " " << s1.capacity() << endl;
	//将s1扩容至50
	s1.reserve(50);
	cout << s1 << " " << s1.capacity() << endl;
	//将s1缩容至20
	s1.reserve(20);
	cout << s1 << " " << s1.capacity() << endl;
	//将s1缩容至10
	s1.reserve(10);
	cout << s1 << " " << s1.capacity() << endl;
	return 0;
}

在这里插入图片描述
在上面的测试中,当使用reserve扩容s1为50时,容量增大到了63;缩容s1为20时,容量没有缩小;缩容s1为10时,容量缩小到了15。均符合上面的叙述。

clear

clear可以清理string对象的字符序列,使其元素个数为0
在这里插入图片描述

int main()
{
	string s1("abcdef");
	cout << s1 << " " << s1.size() << endl;
	s1.clear();
	cout << s1 << " " << s1.size() << endl;
	return 0;
}

在这里插入图片描述

empty

empty用于判断string对象的字符序列是否为空
在这里插入图片描述

int main()
{
	string s1("abcdef");
	cout << s1 << " " << s1.empty() << endl;
	//清理string后,判断其是否为空
	s1.clear();
	cout << s1 << " " << s1.empty() << endl;
	return 0;
}

在这里插入图片描述

元素访问

在类和对象部分我们知道,在类外是不能访问类对象的属性的,但是对于string类,可以通过一些方式来访问string中的字符序列的元素:

迭代器访问

迭代器(iterator)提供一种通用的方法,使其能够依序访问某个容器所含的各个元素,而又无需暴露该容器的内部表达方式。

迭代器的使用与指针类似,可以定义一个对于某个容器的迭代器变量,它指向该容器类型的对象中的某一元素。这意味着迭代器支持类似于指针的解引用*访问成员->加减+- 等操作。

对于string来说,它迭代器的类型为string::iterator,表示这是对于string的迭代器类型,它就可以指向string对象字符序列中的某个元素(对于string来说迭代器底层就是原生指针):

int main()
{
	string s1("abcdef");
	//使用begin函数返回其第一个字符的迭代器
	string::iterator it = s1.begin();
	cout << *it << endl;
	return 0;
}

在这里插入图片描述
有一些接口可以帮助我们使用迭代器:

begin

begin可以返回string对象中指向第一个元素的迭代器
在这里插入图片描述
通过begin函数返回的迭代器,可以访问string对象中的首元素,当然这个迭代器+1就可以访问下一个元素

int main()
{
	string s1("abcdef");
	//使用begin函数返回其第一个字符的迭代器
	string::iterator it = s1.begin();
	cout << *it << endl;
	//it迭代器自增1,指向下一个元素
	++it;
	cout << *it << endl;
	return 0;
}

在这里插入图片描述

end

end可以返回string对象尾元素的下一个位置的迭代器
在这里插入图片描述
前面提到过,迭代器构成的区间是左闭右开的,所以作为整个字符序列的尾,应该指向的是尾元素的下一个位置。所以对这个返回值解引用就是非法访问,就会导致程序崩溃。

这个方法常用于与begin结合,遍历整个string对象的字符序列:

int main()
{
	string s1("abcdef");
	string::iterator it = s1.begin();
	while (it < s1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

rbegin

rbegin返回一个sting对象尾元素的反向迭代器
在这里插入图片描述
反向迭代器 reverse_iterator,与正向迭代器相反,+1是向前移动,-1是向后移动,不难理解,使用反向迭代器就可以实现倒着遍历string对象。

rbegin的返回值解引用就是sting对象的尾元素:

int main()
{
	string s1("abcdef");
	//使用rbegin函数返回其最后一个个字符的反向迭代器
	string::reverse_iterator rit = s1.rbegin();
	cout << *rit << endl;
	//反向迭代器自增,向前移动
	++rit;
	cout << *rit << endl;
	return 0;
}

在这里插入图片描述

rend

rend返回一个string对象首元素前的位置的反向迭代器

在这里插入图片描述
rend返回的就是倒着遍历string对象时,迭代器区间的右边,是一个开区间,所以指向的是首元素前的位置。

对其解引用就会崩溃,常用于与rdegin结合倒序遍历string对象:

int main()
{
	string s1("abcdef");
	string::reverse_iterator rit = s1.rbegin();
	while (rit < s1.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

范围for

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

范围for可以让我们更简洁的遍历容器中的元素,它依赖迭代器实现:

int main()
{
	string s1("abcdef");
	for (auto e : s1)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

在这里插入图片描述
auto是一种新的类型指示符,可以简化繁琐的类型名的书写,在编译阶段auto会替换为变量的实际类型

当然,范围for这里的auto也可由我们已知的类型(元素的类型)替换:

int main()
{
	string s1("abcdef");
	for (char e : s1)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

当需要在范围for中修改元素的值时,在类型后加上&即可

int main()
{
	string s1("abcdef");
	for (char& e : s1)
	{
		++e;
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

下标访问

使用[]用下标访问string中的元素,实际是运算符重载operator[]的函数调用
在这里插入图片描述
用法上与数组的下标访问类似,不过该方法会做越界检查,而数组的越界会崩溃:

int main()
{
	string s1("abcdef");
	for (int i = 0; i < 6; ++i)
	{
		cout << s1[i] << " ";
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

字符串修改

string提供了一些方法来管理其中的字符序列:

insert

insert可以实现在string对象的pos位置插入一些字符

在这里插入图片描述
insert有许多的重载版本,包括在pos位置插入一个string对象(的部分)、插入一个C字符串(的前n个元素怒)、插入若干个指定字符、在迭代器位置添加若干字符、在迭代器位置添加一块迭代器区间的数据等等:

int main()
{
	string s("ghijkl");

	//在s1的第4个元素的位置插入string对象s
	string s1("abcdef");
	s1.insert(3, s);
	cout << "1: " << s1 << endl;

	//在s2的第四个位置插s的第3个位置的3个元素
	string s2("abcdef");
	s2.insert(3, s, 2, 3);
	cout << "2: " << s2 << endl;

	//在s3的第四个元素位置插入一个常量字符串
	string s3("abcdef");
	s3.insert(3, "hello 66");
	cout << "3: " << s3 << endl;

	//在s4的第四个元素的位置"hello 66"的前5个元素
	string s4("abcdef");
	s4.insert(3, "hello 66", 5);
	cout << "4: " << s4 << endl;

	//在s5的第四个元素位置插入6个'q'
	string s5("abcdef");
	s5.insert(3, 6, 'q');
	cout << "5: " << s5 << endl;

	//在s6的首元素位置插入6个'q'
	string s6("abcdef");
	s6.insert(s6.begin(), 6, 'q');
	cout << "6: " << s6 << endl;

	//在s7的首元素位置插入'q'
	string s7("abcdef");
	s7.insert(s7.begin(), 'q');
	cout << "7: " << s7 << endl;

	//在s8的首元素位置插入s
	string s8("abcdef");
	s8.insert(s8.begin(), s.begin(), s.end());
	cout << "8: " << s8 << endl;

	return 0;
}

在这里插入图片描述

(但是在string对象字符序列中间插入元素时,会发生移动,所以效率不高,尽量少用)

不难发现,string的接口是有些冗余的,这里面的一些接口,并不是必须要使用的

append

append用于在string对象末尾追加一块内容

在这里插入图片描述
与上面类似,append也实现了许多的重载版本,例如在string对象末尾插入一个string对象(的部分)、插入一个C字符串(的前n个元素)、插入若干个指定字符、插入一个迭代器区间中的数据等等

int main()
{
	string s("ghijkl");

	//在s1后追加s
	string s1("abcdef ");//为观察清晰,在sn的末尾加上' '
	s1.append(s);
	cout << "1: " << s1 << endl;

	//在s2后追加s从第一个元素开始的3个元素
	string s2("abcdef ");
	s2.append(s, 0, 3);
	cout << "2: " << s2 << endl;

	//在s3后追加"hello 66"
	string s3("abcdef ");
	s3.append("hello 66");
	cout << "3: " << s3 << endl;

	//在s4后追加"hello 66"的前5个元素
	string s4("abcdef ");
	s4.append("hello 66", 5);
	cout << "4: " << s4 << endl;

	//在s5后追加6个'q'
	string s5("abcdef ");
	s5.append(6, 'q');
	cout << "5: " << s5 << endl;

	//在s6后使用反向迭代器追加倒置的s
	string s6("abcdef ");
	s6.append(s.rbegin(), s.rend());
	cout << "6: " << s6 << endl;
	return 0;
}

在这里插入图片描述

push_back

push_back即在string对象后尾插一个字符
在这里插入图片描述

int main()
{
	string s1("abcdef ");//为观察清晰,在s1的末尾加上' '
	//给s1尾插'q'
	s1.push_back('q');
	cout << "1: " << s1 << endl;
	return 0;
}

在这里插入图片描述

operator+=

operator+=+=的运算符重载,用于在string对象的末尾追加数据
在这里插入图片描述
使用+=,可以在string对象的末尾添加一个string对象、C字符串、一个字符:

int main()
{
	string s("ghijkl");
	//给s1+=s
	string s1("abcdef ");
	s1 += s;
	cout << "1: " << s1 << endl;

	//s2+="qqqqqq"
	string s2("abcdef ");
	s2 += "qqqqqq";
	cout << "2: " << s2 << endl;

	//s3+='q'
	string s3("abcdef ");
	s3 += 'q';
	cout << "3: " << s3 << endl;
	return 0;
}

在这里插入图片描述

pop_back

pop_back用于在string对象的尾删除一个字符

在这里插入图片描述

int main()
{
	//对s1尾删
	string s1("abcdef");
	cout << "0: " << s1 << endl;
	s1.pop_back();
	cout << "1: " << s1 << endl;
}

在这里插入图片描述

erase

erase用于删除string对象中的部分元素
在这里插入图片描述
erase有3个重载版本,可以实现删除string对象pos位置的len个元素(缺省值为npos)、迭代器位置的一个元素、迭代器区间中的元素:

int main()
{
	//删除s1中第四个元素位置的2个元素
	string s1("abcdef");
	s1.erase(3, 2);
	cout << "1: " << s1 << endl;

	//删除第二个元素
	string s2("abcdef");
	s2.erase(s2.begin() + 1);
	cout << "2: " << s2 << endl;

	//删除s3中第二个元素到倒数第二个元素
	string s3("abcdef");
	s3.erase(s3.begin() + 1, s3.end() - 1);
	cout << "3: " << s3 << endl;
	return 0;
}

在这里插入图片描述
但是删除string对象字符序列中间的元素时,会发生移动,所以效率不高,尽量少用

字符串查找

find

find用于在string对象中从指定位置向后查找子串

在这里插入图片描述
find有4个重载版本,其中参数pos为查找的起始位置。查找到则返回子串在原串中的位置,否则返回npos(-1u)
需要注意的是,当寻找子串时,必须与原串中完全匹配才会返回值。

借助find,可以实现将一个string对象依据某字符串分割(一个网址的分割):

int main()
{
	string web("https://legacy.cplusplus.com/reference/string/string/find/");
	size_t b1 = web.find("://");
	if (b1 != string::npos)
	{
		size_t b2 = web.find("/", b1 + 3);
		if (b2 != string::npos)
		{
			string protocol(web.begin(), web.begin() + b1);
			string domain(web.begin() + b1 + 3, web.begin() + b2);
			string uri(web.begin()+ b2 + 1, web.end());
			
			cout << protocol << endl;
			cout << domain << endl;
			cout << uri << endl;
		}
	}
}

在这里插入图片描述

rfind

find用于在string对象中从指定位置向前查找子串

在这里插入图片描述
rfindfind的区别只在于从 pos位置开始向前寻找,在这里就不再赘述了

非成员函数

operator>>

operator>>是流提取运算符重载,借助该运算符重载可以实现使用cin >> 方便的输入string对象
在这里插入图片描述

operator<<

operator<<是流插入运算符重载,借助该运算符重载可以实现使用cout << 方便的输入string对象
在这里插入图片描述

int main()
{
	string s;
	cin >> s;
	cout << s << endl;
	return 0;
}

在这里插入图片描述

总结

到此,关于string类主要接口的介绍及使用就介绍完了
熟练使用string会使我们在处理字符序列数据时更加方面

接下来的文章中将模拟实现string类的主要接口,欢迎大家持续关注

如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出

如果本文对你有帮助,希望一键三连哦

希望与大家共同进步哦

  • 24
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 19
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿qiu不熬夜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值