C++ STL --- string类用法

目录

1.构造

2.容量

  (1)reserve使用及性质验证

    [1]扩容机制验证

    [2]扩容机制总结

  (2)resize使用及性质验证

3.迭代器

4.元素访问

5.修改

6.特殊操作

7.string类的输入输出

  (1)支持cin和cout

  (2)在oj中的使用


        string类是C++STL中的序列形容器之一,它是动态类型的顺序表,只能存储char类型的字符。使用时需要包含头文件 "string"。

        string类主中的操作主要分为六大模块,构造、容量、元素访问、迭代器、修改、特殊操作。

在本文中只讲解最常用的一些函数(因为它的函数实在太多了)。

        本文由于是介绍string类函数的用法,因此绝大部分内容都在代码中,文字叙述较少。(本文代码均在win10系统下的vs2019验证)

1.构造

        string类对象常用的五种构造如下表:

函数名称功能说明
string()构造空的string对象,即空字符串
string(const char* s)用C语言中的字符串构造string类对象
string(size_t n,char c)string类对象中包含n个字符c
string(const string& s)拷贝构造函数
string(const char* s,size_t n)用C语言字符串的前n个字符构造string类对象

        代码一:在此代码中展示以上五种构造函数的使用方法。

        代码一:可以看到string类对象可以直接用cin>>来接收键盘输入的字符串,也可以用cout直接输出string类对象的内容。但是使用cin>>直接接收时,碰到空格会停止接收。

//代码一
#include "iostream"
#include "string"
using namespace std;

void Test() {
	string s1;//构造空的string对象,即空字符串

	const char* s = "abcde";//C语言中的字符串
	string s2(s);//用C_string来构造string类对象

	string s3(10, 'A');//string类对象中包含10个字符‘A’

	string s4(s3);//拷贝构造函数

	string s5(s, 3);//用C语言字符串的前3个字符构造string类对象

	cin >> s5;
	cout << s5;
}
int main() {
	Test();
}

2.容量

        string类在容量这一模块中的操作较多,其中有一部分的函数还具有自己的特性需要进行分析。常用的容量相关的操作如下表:

函数名称功能说明
size()返回字符串有效字符长度
length()返回字符串有效字符长度
capacity()返回总空间的大小
empty()检测string类对象是否是空的,是返回true,否则返回false
clear()清空有效字符
reserve(size_t n)为string类对象总空间大小进行重新设置
resize(size_t n,char c)将有效字符个数修改为参数中的n个,多出的空间用参数中的c填充,如果没有第二个参数,填充为0

        注意:总空间是指string对象中一共有多少空间(容量)可以用来存储char类型数据,有效元素个数是指在总空间中有多少空间都存放了有效的char类型数据。

        前五个函数较为简单,利用下述代码来展示用法:

        代码二:s1.capacity() == 15 注意这个15,下面这个是要讲一讲的。

//代码二
#include "iostream"
#include "string"
using namespace std;

void Test() {
	string s1("abcde");
	cout << s1.size() << endl;//输出 5
	cout << s1.length() << endl;//输出 5
	cout << s1.capacity() << endl;//输出 15
	cout << s1.empty() << endl;//输出 0
	s1.clear();
	cout << s1.empty() << endl;//输出 1
}
int main() {
	Test();
}

  (1)reserve使用及性质验证

        reserve(size_t n)这个函数的作用是根据参数n来对string类对象的容量进行合适的改变(增长或缩小)。这个合适的就很有意思了,因为并不是n等于多少,它就一定把容量变成多少。

        首先来看一下string类的大小,如代码三:

        代码三:string类的大小是28,那么string类中都有什么?答案是:一个char*类型的变量,一个size_t类型的size,一个size_t类型的capacity,并且还维护了一个大小是16的数组(但在我的测试中发现,如果string类对象的字符数小于等于15个,容量是15。当字符数等于16时,容量变成31,发生扩容,所以推测第16个字节应该是为了存储'\0')。

//代码三
#include "iostream"
#include "string"
using namespace std;

int main() {
	cout << sizeof(string) << endl;//输出 28
}

    [1]扩容机制验证

        用下面两段代码验证 reserve(size_t n) 合适的扩容机制;

        代码四:当初始字符串中元素个数小于等于15时的扩容机制。

        这段代码较长,但要仔细看完。此时字符串长度是10,可以看到,扩容后的容量并不总是和传递的实参相等,通常情况下是要大一些的。

        还有一个现象是,在将参数n缩小后,发现刚开始的时候容量依然保持70不变,当n缩小到15后,容量变为15。当n的大小小于字符串长度后,容量保持不变,一直是15。

        同时观察扩容的容量大小增加情况:31 -> 47 -> 70。(说明vs2019中大概是按1.5倍的方式递增)

//代码四
#include "iostream"
#include "string"
using namespace std;

void Test() {
	string s1("abcde12345");

	s1.reserve(20);
	cout << s1.size() << endl;//10
	cout << s1.capacity() << endl;//31

	s1.reserve(30);
	cout << s1.size() << endl;//10
	cout << s1.capacity() << endl;//31

	s1.reserve(40);
	cout << s1.size() << endl;//10
	cout << s1.capacity() << endl;//47
	
	s1.reserve(50);
	cout << s1.size() << endl;//10
	cout << s1.capacity() << endl;//70

	s1.reserve(60);
	cout << s1.size() << endl;//10
	cout << s1.capacity() << endl;//70

	s1.reserve(50);
	cout << s1.size() << endl;//10
	cout << s1.capacity() << endl;//70

	s1.reserve(40);
	cout << s1.size() << endl;//10
	cout << s1.capacity() << endl;//70

	s1.reserve(30);
	cout << s1.size() << endl;//10
	cout << s1.capacity() << endl;//70

	s1.reserve(20);
	cout << s1.size() << endl;//10
	cout << s1.capacity() << endl;//70

	s1.reserve(15);
	cout << s1.size() << endl;//10
	cout << s1.capacity() << endl;//15

	s1.reserve(12);
	cout << s1.size() << endl;//10
	cout << s1.capacity() << endl;//15

	s1.reserve(9);
	cout << s1.size() << endl;//10
	cout << s1.capacity() << endl;//15
}

int main() {
	Test();
}

        代码五:当初始字符串中元素个数大于15时的扩容机制。

        此时字符串长度是0。可以看到,在n逐渐增加的过程中,在vs2019中也基本按照1.5倍大小扩容。但是在这一次缩小到15后,容量并没有变小。下面来总结一下它的性质。

//代码五
#include "iostream"
#include "string"
using namespace std;

void Test() {
	string s1("abcde12345abcde12345");

	s1.reserve(20);
	cout << s1.size() << endl;//20
	cout << s1.capacity() << endl;//31

	s1.reserve(30);
	cout << s1.size() << endl;//20
	cout << s1.capacity() << endl;//31

	s1.reserve(40);
	cout << s1.size() << endl;//20
	cout << s1.capacity() << endl;//47
	
	s1.reserve(50);
	cout << s1.size() << endl;//20
	cout << s1.capacity() << endl;//70

	s1.reserve(60);
	cout << s1.size() << endl;//20
	cout << s1.capacity() << endl;//70

	s1.reserve(50);
	cout << s1.size() << endl;//20
	cout << s1.capacity() << endl;//70

	s1.reserve(40);
	cout << s1.size() << endl;//20
	cout << s1.capacity() << endl;//70

	s1.reserve(30);
	cout << s1.size() << endl;//20
	cout << s1.capacity() << endl;//70

	s1.reserve(20);
	cout << s1.size() << endl;//20
	cout << s1.capacity() << endl;//70

	s1.reserve(15);
	cout << s1.size() << endl;//20
	cout << s1.capacity() << endl;//70

	s1.reserve(12);
	cout << s1.size() << endl;//20
	cout << s1.capacity() << endl;//70

	s1.reserve(9);
	cout << s1.size() << endl;//20
	cout << s1.capacity() << endl;//70
}

int main() {
	Test();
}

    [2]扩容机制总结

        1. reserve(size_t n) 不会改变有效元素的个数。(这就是为什么代码五中当n变为15时,容量不变,要是容量变成15,有效元素不就变少了吗?)

        2.reserve(size_t newcapacity),假设当前容量是 oldcapacity。分情况总结:

        newcapacity > oldcapacity:reserve函数扩容。(在vs2019中大概按照1.5倍扩容)

        newcapacity < oldcapacity:<1>字符串有效长度 <= 15,将容量缩小为15。<2>字符串有效长度 > 15,容量保持不变。

        有没有发现这几条性质都要围绕15这个数字展开,就是因为之前说过的,string类对象内部存在一个定长char类型数组,可以存放15个有效字符。当字符串要扩容,就要去堆上申请空间。当空间要缩小时,很多时候并不能成功,原因就是,申请空间不容易,编译器为了防止缩小后又扩容浪费时间,所以一般情况下就不会缩小。

  (2)resize使用及性质验证

        上面的reserve只是用来管理容量,现在的resize是专门用来管理有效字符长度的。

        它有两种使用方法:1.resize(size_t n) 将有效元素字符修改成n个,多出的空间用'\0'填充。2.resize(size_t n,char c)将有效字符修改为n个,多处的空间用字符c填充。

        代码六:

//代码六
#include "iostream"
#include "string"
using namespace std;

void Test() {
	string s1("123");
	s1.resize(10,'d');
	cout << s1.size() << endl;// 10

	s1.resize(2);
	cout << s1.size() << endl;// 2
}

int main() {
	Test();
}

3.迭代器

        在STL初级阶段,我们就把迭代器当作指针来使用即可。

函数名称功能说明
begin()返回string对象首字符的迭代器
end()返回string对象最后一个字符下一个位置的迭代器
rbegin()返回string对象最后一个字符的迭代器
rend()返回string对象首字符前一个位置的迭代器

        下图是正反迭代器的示意图:

        代码七:既然把迭代器当作指针使用,自然是可以解引用的。

//代码七
#include "iostream"
#include "string"
using namespace std;

void Test() {
	string s("abcde");
	auto it1 = s.begin();
	auto it2 = s.end();
	auto it3 = s.rbegin();
	auto it4 = s.end();
	while (it1 != it2) {
		cout << *it1 << " ";
		it1++;
	}
}

int main() {
	Test();//输出 a b c d e
}

4.元素访问

函数名称功能说明
operatoe[size_t pos]返回pos位置的字符,const string类对象调用
at(szie_t pos)返回pos位置的字符
范围for和数组中的范围for使用方法相同

        代码八:注意,当string对象用const修饰时,不可以使用 s[0]='k' 及类似的方式修改string对象。因为const对象是不可以修改的。

//代码八
#include "iostream"
#include "string"
using namespace std;

void Test() {
	string s("abcde");
	cout << s[0] << endl;//输出 a
	cout << s.at(0) << endl;//输出 a
	for (auto& e : s) {
		cout << e;
	}//输出 abcde
	s[0] = 'k';
}

int main() {
	Test();
}

5.修改

函数说明功能说明
push_back(c)在string类对象后尾插字符c
append(const char* s)在string类对象后追加字符串s
append(size_t n,char c)在string类对象后追加n个字符c
operator += c 或 str 或 string类对象在string类对象后追加字符c 或 字符串str 或 string类对象
insert(iterator it,char c)在迭代器it的位置插入字符c
insert(size_t pos,const char* s)在下标为pos的位置插入字符串s

substr(size_t pos,size_t n)

从string类对象的pos位置开始向后复制n个元素
earse(size_t pos,size_t n)在string类对象从pos位置开始向后删除n个元素
erase(iterator firest,iterator last)在string类对象中删除 [first,last) 之间的元素,注意区间是左闭右开,last位置不能删除。

        代码九:

//代码九
#include "iostream"
#include "string"
using namespace std;

//插入
void Test1() {
	string s1("123");
	string s2("89");
	const char* str = "56";

	s1.push_back('4');

	s1.append(str);
	s1.append(1, '7');

	s1 += s2;
	s1 += '0';
	
	cout << s1 << endl;//输出 1234567890

	const char* st = "bc";
	s1.insert(s1.begin(), 'a');
	s1.insert(1, st);
	cout << s1 << endl;//输出 abc1234567890
}

//复制子串与删除
void Test2() {
	string s1("1234567");
	string s2 = s1.substr(0, 7);
	cout << s2 << endl;//输出 1234567

	s1.erase(0, 5);
	cout << s1 << endl;//输出 67
	s2.erase(s2.begin(), s2.end() - 3);
	cout << s2 << endl;//输出 567
}

int main() {
	Test1();
	Test2();
}

6.特殊操作

函数说明功能说明

find(char c,size_t pos)

npos

在string类对象从pos位置开始向后查找字符c,遇见第一个c返回下标,若没找到返回npos

如果pos没有传递,默认从0下标开始查找

find(const string& s,size_t pos)在string对象中从pos位置开始向后查找子串s出现的首个位置的下标,找不到返回npos
rfind(char c,size_t pos)

在string类对象从pos位置开始向前查找字符c,遇见第一个c返回下标,若没找到返回npos

若pos没有传递,默认从尾元素开始

c_str()返回一个指向正规C字符串的指针常量, 内容与本string串相同。(为了兼容C语言)
atoi(const char* s)将字符串s转换为对应的整型变量
sort(iterator firest,iterator last)把string类对象 [first,last) 之间的元素按大小排序

        代码十:在使用函数时,一定要特别注意参数究竟是普通类型还是const类型。

//代码十
#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
#include "string"
using namespace std;

void Test() {
	string s1("1234");
	cout << s1.find('2', 0) << endl;//输出 1

	const string s2("23");
	cout << s1.find(s2, 0) << endl;//输出1

	//rfind与find区别仅是方向的区别,故不做演示

	const char* str = s1.c_str();
	int num1 = atoi(str);
	cout << num1 << endl;//输出 1234

	//也可以利用strcpy函数
	char ss[100];
	strcpy(ss, s1.c_str());
	int num2 = atoi(str);
	cout << num2 << endl;//输出 1234
}

int main() {
	Test();
}

7.string类的输入输出

  (1)支持cin和cout

        代码十一:使用cin接收时,遇到空格等空白字符和回车后停止接收。可以使用cout直接打印。

//代码十一
#include "iostream"
#include "string"
using namespace std;

void Test() {
	string s;
	cin >> s;
	cout << s;
}

int main() {
	Test();
}

  (2)在oj中的使用

        代码十二:在刷oj题中经常会遇到需要循环输入的题目,这时候就需要掌握相应的使用方法。

//代码十二
#include "iostream"
#include "string"
using namespace std;

//循环接收单个单词并打印
void Test1() {
	string word;
	while (cin >> word) {
		cout << word <<endl;
	}
	//截止输入 ctrl + z 回车
}

//循环接收带有空格的字符串并打印
void Test2() {
	string words;
	while (getline(cin,words)) {
		cout << words << endl;
	}
	//截止输入 ctrl + z 回车
}

int main() {
	Test1();
	Test2();
}

  • 23
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 18
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值