【C++】string类中的常用成员函数介绍

C++ string类常用函数详解

目录

1 为什么要学习string类

1.1 string类的优势

2 标准库中的string类

2.1 string类对象的常见构造

2.2 string类对象的访问及遍历操作

2.2.1 string类对象的访问

2.2.2 三种遍历方式

2.2.2.1 size()获取长度

2.2.2.2 利用迭代器

迭代器

反向迭代器

const迭代器

const反向迭代器

2.2.2.3 范围for

2.3 string类对象的容量操作

2.3.1 计算字符串长度

2.3.2 空间扩容

2.3.3 clear()、reserve()、rsize()

2.4 string类对象的修改操作

2.4.1 插入字符

2.4.2 查找函数

2.4.3 返回子串


1 为什么要学习string类

string类是C++中处理字符串的核心工具,在常规工作中,使用string类方便快捷,很少用C语言库中的字符串操作函数。

在C语言中,字符串是用字符数组表示的,以\0结尾,这样的方式存在一些问题:

  • 首先是容易出错,需要自己手动管理内存,容易发生缓冲区溢出
  • 其次是操作较为复杂,像使用strcpy,strcat,如果不熟练容易出错
  • 再者字符数组是固定大小的,无法自动调整

C语言风格的字符串:

//C语言
char str1[20] = "hello world";
char str2[] = "HELLO WORLD";
char str3[50];//确保空间足够大
strcat(str3, str1);
strcat(str3, str2);//有风险,可能会导致溢出

C++风格的字符串:

//C++
#include <string>
std::string str1 = "hello world";
std::string str2 = "HELLO WORLD";
std::str3 = str1 + str2;//简洁安全

1.1 string类的优势

(1)自动内存管理:

string类自动处理内存的分配和释放,无需担心内存泄漏和溢出等问题。

(2)丰富的成员函数:

string类中具有丰富的成员函数,可以调用成员函数来对字符串进行操作,例如:

查找:find(),refind()

修改:append(),insert(),erase(),replace()

子串:substr()

大小操作:resize(),capacity()

(3)支持运算符重载

可以使用+、-、+=、==、<等运算符,使代码更直观。

总而言之,string类在安全性、便利性、可读性等方面相较于C语言有很大优势,这就是我们要学习string类的原因。

2 标准库中的string类

string类中有很多接口,本文只说明一些常用的接口,具体可以参考string类的文档:https://cplusplus.com/reference/string/string/?kw=string

2.1 string类对象的常见构造

string()构造空的string类对象
string(const char* s)用C-string来构造string类对象
string(const string&s)拷贝构造函数
void teststring1()
{
	string s1;//构造一个空的string类对象s1
	string s2("helllo world");//用C格式字符串构造string类对象s2
	string s3 = s2;//拷贝构造上s3
}

2.2 string类对象的访问及遍历操作

operator[ ]返回pos位置的字符,const string类对象调用
begin+endbegin获取第一个字符的迭代器,end获取最后一个字符下一个位置的迭代器
rbegin+rendrbegin获取字符的最后一个位置,rend获取字符第一个位置的前一个位置
范围forC++11中支持更简洁的新遍历方式

2.2.1 string类对象的访问

使用[ ]操作符访问某一个字符的位置,可以修改某一个字符:

void  teststring1()
{
	string s1("hello world");
	s1[1] = 'x';//修改下标1的字符
	cout << s1 << endl;//输出hxllo world
}

int main()
{
	teststring1();
	return 0;
}

2.2.2 三种遍历方式

2.2.2.1 size()获取长度
void  teststring1()
{
	string s1("hello world");
	s1[1] = 'x';//修改下标1的字符
	//size()获取长度 []访问字符
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i];
	}
	cout << endl;
}
2.2.2.2 利用迭代器
迭代器

string的迭代器是STL中一种类似指针的对象,用于遍历和访问字符串中的字符,提供了一种统一的方式来访问容器元素。

迭代器就像是一个"智能指针",可以指向 string 中的某个字符,并支持移动操作来遍历整个字符串。所有容器都有自己的迭代器,可以用类似的方式访问。

void teststring2()
{
	string s("hello world");
	string::iterator it = s.begin();
	while (it != s.end())
	{
		//*it += 2;//可以修改
		cout << *it;
		++it;
	}
	cout << endl;
}

这里的string::iterator可以用auto来代替,结果是一样的

void teststring2()
{
	string s("hello world");
	auto it = s.begin();
	while (it != s.end())
	{
		//*it += 2;//可以修改
		cout << *it;
		++it;
	}
	cout << endl;
}

auto是一个关键字,在C++11中,auto是一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译期间推导而得。

  • 用auto声明指针类型时,用auto和auto*没有区别,但是auto声明引用类型时必须用auto&
  • 在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器会报错。
  • auto不能作为函数的参数,可以做返回值
  • auto不能用来声明数组
反向迭代器

使用反向迭代器可以倒着访问字符串

void teststring2()
{
	string s("hello world");
	string::reverse_iterator rit = s.rbegin();//rbegin返回字符串的最后一个位置
	while (rit != s.rend())//rend返回字符串第一个字符的前一个位置
	{
		//*rit += 2;//可以修改
		cout << *rit;
		++rit;//从最后一个位置向前走,倒着走,反向迭代器重载了++操作符
	}
	cout << endl;
}
const迭代器

详情见注释

void teststring2()
{
	string s("hello world");
	//const正向迭代器 本身可以修改 指向的内容不可以修改
	string::const_iterator cit = s.begin();
	while (cit != s.end())
	{
		//*cit += 2;  错误!const迭代器不可以修改指向的内容
		cout << *cit;
		++cit;
	}
	cout << endl;
}
const反向迭代器
void teststring2()
{
	string s("hello world");
	//const反向迭代器 本身可以修改 指向的内容不可以修改 从后向前遍历
	string::const_reverse_iterator rcit = s.rbegin();
	while (rcit != s.rend())
	{
		//*rcit += 2; 错误!const反向迭代器不可以修改指向的内容
		cout << *rcit;
		++rcit;
	}
}

像string::const_reverse_iterator 这种规定好的长类型变量就可以用auto代替,结果都是一样的,这就体现出了auto的优势,缺点就是牺牲了可读性。

2.2.2.3 范围for

C++11中引入了基于范围的for循环,for循环后的括号由冒号:分为两部分:冒号前部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束

  • 范围for可以作用到数组和容器对象上进行遍历
  • 范围for的底层其实就是替换为迭代器
//范围for底层就是迭代器
//自动从s中寻找值给ch auto是自动推导
//自动迭代,自动判断结束
for (auto& ch : s)
{
	ch += 2;
	cout << ch;
}
cout << endl;
cout << s << endl;//返回auto引用才能修改s2,ch是s2中字符的别名,不返回引用不会修改s

以上就是本节遍历字符串的三种方式,其中范围for和迭代器是重点。

2.3 string类对象的容量操作

size返回字符串有效字符长度
length返回字符串有效字符长度
capacity返回空间总大小
empty检测字符串是否为空串,是返回true,否则返回false
clear清空有效字符
reserve为字符串预留空间
resize将有效字符的个数改成n个,多出的空间用字符c填充

2.3.1 计算字符串长度

计算字符串长度时主要有两个成员函数,分别是size()和length()。它们返回实际存储的字符数,不依赖\0做标志,字符串中可以包含\0作为有效字符。

void teststring3()
{
	string s1 = "hello world";
	cout << s1.size() << endl;//输出11
	string s2 = "hello w\0orld";
	cout << s2.length() << endl;//输出7
}

2.3.2 空间扩容

本小节利用字符串中尾插新的数据来说明剩余的string类中对容量改变的成员函数。

#include <iostream>
#include <string>
using namespace std;
void TestPushBack()
{
	string s;
	size_t sz = s.capacity();//看空间
	cout << "capacity changed: " << sz << endl;
	for (size_t i = 0; i < 100; i++)
	{
		s.push_back('c');//插入字符的成员函数
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << endl;
		}
	}
}
int main()
{
	TestPushBack();
	return 0;
}

输出结果:

可以看到在Visual Studio中,内存中先开15个字节的空间,避免短字符串的动态分配,然后2倍扩容,快速扩大容量,减少分配次数,接下来的扩容都是依据原来的容量进行1.5倍扩容,使得内存利用率更高,节省一些空间。

2.3.3 clear()、reserve()、rsize()

reserve(),括号内加数字,表示预留多少的空间,这样提前开空间,避免扩容,提高效率。

#include <iostream>
#include <string>
using namespace std;
void TestPushBack()
{
	string s;
	s.reserve(100);
	size_t sz = s.capacity();//看空间
	cout << "capacity changed: " << sz << endl;
	for (size_t i = 0; i < 100; i++)
	{
		s.push_back('c');//插入字符的成员函数
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << endl;
		}
	}
}
int main()
{
	TestPushBack();
	return 0;
}

输出结果:

可以看到提前预留空间只进行了一次扩容,提高了性能。但是使用reserve()要注意如果过度预留空间会造成资源浪费,不要在不了解数据特征时盲目使用

clear()是清理字符的内容,但是不清空间。

#include <iostream>
#include <string>
using namespace std;
void teststring4()
{
	string s("hello world");
	s.reserve(100);
	cout << s.capacity() << endl;
	s.clear();
	cout << s << endl;
	cout << s.capacity() << endl;

}
int main()
{
	teststring4();
	return 0;
}

输出结果:

resize()是将有效字符个数改成n个,多出的字符空间用指定字符填充,如果不指定,用\0填充

#include <iostream>
#include <string>
using namespace std;
void teststring5()
{
	string s("hello world");
	s.resize(7);
	cout << s << endl;
	s.resize(15);//不指定字符,默认用\0
	cout << s << endl;
	s.resize(20,'c');
	cout << s << endl;
}
int main()
{
	teststring5();
	return 0;
}

输出结果:

resize(7):截断字符串,保留前7个字符

resize(15):扩大到15,用 \0 填充(输出时不可见)

resize(20, 'c'):继续扩大到20,用'c' 填充额外的5个位置

string的resize函数中,小于size时,会删除数据,容量不会缩小,大于size时,插入数据,如果空间不够就扩容

2.4 string类对象的修改操作

push_back在字符串中尾插字符
append在字符串后追加一个字符串
operator+=在字符串后追加字符串str
c_str返回C格式字符串
find+npos从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr从str中从pos位置开始,截取n个字符,然后将其返回

2.4.1 插入字符

可以使用push_back、append、+=来尾插,其中+=最常用

void teststring6()
{
	string s("hello world");
	s.push_back('x');
	s.append("ttt");
	//+=常用
	s += 'c';
	s += "yyy";
	cout << s << endl;
}
int main()
{
	teststring6();
	return 0;
}

头插用insert()来在字符串头部或某一位置插入字符或者字符串,头删和指定位置删除用erase(),操作如下:

void teststring7()
{
	string s("hello world");
	s.insert(0,"x");//头插x
	s.insert(0,"xxx");//头插字符串xxx
	s.insert(14, "x");//在下标n处插入x
	s.insert(0,1,'c');//头插1个字符c
	s.insert(0,2,'c');//头插两个字符cc
	s.insert(10, 2, 'c');//下标n处插入两个字符cc
	s.erase(0, 1);//头删一个字符
	s.erase(s.begin());//利用迭代器。头删
	s.erase(--s.end());//尾删
	cout << s << endl;
}

头插头删操作建议谨慎使用,在指定位置插入字符时,后面的数据先向后挪动,头删时后面的数据向前挪动,这样会降低效率。

2.4.2 查找函数

find()函数从pos位置查找字符串中的字符或指定字符串,如果找到,返回第一个匹配的字符的下标位置(可能存在多个匹配字符),如果没有找到,返回npos。rfind()是从后向前找。

void teststring8()
{
	string s("hello wo rld");
	size_t pos = s.find(' ');
	while (pos != string::npos)//找空格
	{
		s.replace(pos, 1, "%%");
		pos = s.find(' ');
	}
	cout << s << endl;//输出hello%%wo%%rld

}

2.4.3 返回子串

substr是从str中从pos位置开始,截取n个字符,然后将其返回。

void teststring9()
{
	string s("test.cpp.rar");
	size_t pos = s.rfind('.');//从后向前找
	string suffix = s.substr(pos);//获取.和它之后的字串
	cout << suffix << endl;//输出.rar

	size_t pos2 = s.find('.');//从前向后找
	string suffix2 = s.substr(pos2);//获取.和它之后的字串
	cout << suffix2 << endl;//输出.cpp.rar
}

以上就是有关string类的常用成员函数说明,具体了解全部的成员函数可以查看文档,链接在本文开头给出,如果这篇文章对你有用,可以点点赞哦,你的支持就是我写下去的动力,后续会继续更新其他知识。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值