c++中(string类)

一.标准库中的string类

1.1string类了解

1. 字符串是表示字符序列的类

2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作 单字节字符字符串的设计特性。

3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信 息,请参阅basic_string)。

4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits 和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。

5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个 类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

 总结:

1. string是表示字符串的字符串类

2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。

 3. string在底层实际是:basic_string模板类的别名,typedef basic_string string;

4. 不能操作多字节或者变长字符的序列。

 在使用string类时,必须包含#include头文件以及using namespace std;

1.2string类的常用接口说明

1.string类对象的常见构造

          

#include<iostream>
#include<string>
using namespace std;

int main()
{
	string s1;

	string s2("hello string");

	string s3(s2);

	string s4(10, 'A');

	//string的对象可以直接用cout打印
	cout << s2 << endl;
	cout << s3 << endl;
	cout << s4 << endl;
	return 0;
}

string就是动态类型的顺序表----》元素类型是char

2. string类对象的容量操作

      

注意:

1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一 致,一般情况下基本都是用size()。

2. clear()只是将string中有效字符清空,不改变底层空间大小。

3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字 符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的 元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大 小,如果是将元素个数减少,底层空间总大小不变。

4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于 string的底层空间总大小时,reserver不会改变容量大小。

void TestString1()
{
	string s("hello");
	cout << s.size() << endl;
	cout << s.length() << endl;
	cout << s.capacity() << endl;

	if (!s.empty())
	{
		cout << "s is not empty:" << s<<endl;
	}
	else
	{
		cout << "s is empty:" << endl;
	}

	s.clear();
	cout << s.size() << endl;
	if (!s.empty())
	{
		cout << "s is not empty:" << s << endl;
	}
	else
	{
		cout << "s is empty:" << endl;
	}
}

 reserve:(size_t newcapacity)用来进行扩容的

//reserve(size_t newcapacity):
void TestString2()
{
	string s("hello");

	cout << s.size() << endl;
	cout << s.capacity() << endl;

	s.reserve(20);
	cout << s.size() << endl;
	cout << s.capacity() << endl;

	s.reserve(35);
	cout << s.size() << endl;
	cout << s.capacity() << endl;

	s.reserve(60);
	cout << s.size() << endl;
	cout << s.capacity() << endl;

	s.reserve(100);
	cout << s.size() << endl;
	cout << s.capacity() << endl;

	s.reserve(80);
	cout << s.size() << endl;
	cout << s.capacity() << endl;

	s.reserve(50);
	cout << s.size() << endl;
	cout << s.capacity() << endl;

     s.reserve(20);
	cout << s.size() << endl;
	cout << s.capacity() << endl;


    s.reserve(15);
	cout << s.size() << endl;
	cout << s.capacity() << endl;


    s.reserve(10);
	cout << s.size() << endl;
	cout << s.capacity() << endl;


}

 

 

由上图结果可知,当s.reserve增大时 容量不断增大。

但是当 s.reserve减小时,空间容量却不变。

但是当s,reserve缩小到一个临界点时,空间容量却减小了

解释:

通过reserve(size_t newcapacaity)将string底层的空间扩大

假设string底层旧空间的大小为oldcapacity

如果newcapacity>oldcapacity:空间确实会变大,扩容具体做法:

string类内部会按照一定的机制跟进用户所传递的容量去计算新的容量

扩容机制:开辟新空间  拷贝元素 释放旧空间

如果newcapacity<=oldcapacity && newcapacity>=16

reserve会忽略本次操作,即不会扩容缩小

newcapacity<16 &&  有效元素个数小于16   :reserve才会将底层空间大小缩小

否则忽略本次操作

注意:string是在vs2019下验证的

为什么时newcapacity小于16时候,reserve才会真正将空间缩小?

在vs使用STL版本中:PJ版本

string对象内部包含一个固定大小的数组----> char buff[16]

原因:绝大多数情况下的字符串中有效个数都是小于16  比如:人名  单词等

直接使用固定大小的数组  不从堆上开辟空间可以让string类的效率更高

当有效元素个数超过16时,string类才需要真正从堆上开辟空间之后一般不会再将空间缩小


resize(size_t newsize,char ch = '\0'):将有效元素个数修改到newsize,如果newsize大于string对象中原有字符的个数多出的字符使用ch填充。

void TestString04()
{
	string s("hell0");
	s.resize(10, 'A');
	s.resize(20, 'B');
	s.resize(40, 'C');


	s.resize(30);
	s.resize(20);
	s.resize(17);
	s.resize(16);
	s.resize(15);
	s.resize(15);
}

 

如果newsize大于string对象中原有字符的个数多出的字符使用ch填充。并且会进行扩容,然后使用ch填充多出来的字符。

 

 注意:如果是将元素个数增多,可能会改变底层容量的大 小,如果是将元素个数减少,底层空间总大小不变。

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

       

 

 迭代器就当成指针来想,怎么使用指针就怎么使用迭代器

类似于:typedef char* iterator   指针可以++ 可以解引用迭代器当然也可以

for循环   或者范围for来遍历string的

迭代器更多的是给算法使用  比如:要将string对象逆置

void TestString11()
{
	string s("123456789");

	//string遍历
	//更推荐使用下表:也就是for循环+下表的方式
	for (size_t i = 0; i < s.size(); ++i)
	{
		cout << s[i];
	}
	cout << endl;

	//其次可以使用范围for
	for (auto e : s)
		cout << e;
	cout << endl;

	//也可以使用迭代器遍历,不过对于string,一般情况下不太推荐
	string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it;
		++it;
	}
	cout << endl;

	string::reverse_iterator rit = s.rbegin();
	while (rit != s.rend())
	{
		cout << *rit;
		++rit;
	}
	cout << endl;
}

string在遍历的时候,很少用迭代器,更多是跟算法结合

逆置:reverse

 排序:sort

#include<algorithm>
void TestString13()
{
	string s("123456789");
	reverse(s.begin(), s.end());
	cout << s << endl;

	sort(s.begin(), s.end());
	cout << s << endl;
}

 4. string类对象的修改操作

      

operator+=:在string之后拼接:单个字符  c格式的字符串   string对象

append:在string之后拼接:单个字符  c格式的字符串  string对象

push_back(char ch):往string尾部插入ch

void TestString06()
{
	string s("hello");
	s.push_back(' ');

	//拼接c格式的字符串
	s += "world";
    
	//拼接string对象
	string ss("!!");
	s += ss;

	s += '$';
}

void TestString07()
{
	string s("hello");

	s.append(1, ' ');  //等价的 s.push_back(' ') || s+=''
	
	s.append("world");   //s+="world";

	string ss("!!!");
	s.append(ss);     //s+=ss;

   	string str("123456");
	s.append(str, 1, 3);  //将str内部234拼接到s中
}


void TestString08()
{
	string s("hello");

	//在0号位置插入3个 'A'
	s.insert(0, 3, 'A');

	s.insert(0, "bit");

	string ss("BBBB");
	s.insert(7, ss);

	s.insert(s.begin(), ss.begin(), ss.end());
}

 erase:删除

void TestString09()
{
	string s("hello world");

	//从0号位置开始删除六个
	s.erase(0, 6);

	//删除从begin开始+2位置的字符
	s.erase(s.begin() + 2);

	s.erase(s.begin(), s.end());   //s.clear()

}

 借助push_back方法演示string的扩容机制

 在vs2019中:string是按照1.5倍的方式进行扩容

 在linux中:SGI版本STL的string是按照2倍方式进行扩容

上述代码还存在缺陷:效率很低

原因:扩容是需要---》申请新空间  拷贝元素  释放旧空间   虽然元素不断增多 拷贝成本增大

优化:

void TestString10()
{
	string s;
	s.reserve(300);
	size_t cap = s.capacity();
	for (size_t i = 0; i < 300; ++i)
	{
		//s+='A';
		s.push_back('A');
		if (cap != s.capacity())
		{
			cap = s.capacity();
			cout << cap << endl;
		}
	}

}

atoi:int atoi(const char* str )   转换整形的数字

c_str():返回string对象中存储字符串的那块空间的首地址

void TestString15()
{
	string s("12345");
	int ret=atoi(s.c_str());
	cout << ret << endl;
}

 find:

//需求:把字符串中1替换成0
void TestString16()
{
	string s("1234523471136");

	size_t pos = 0;
	while (true)
	{
		pos=s.find('1', pos = 0);   //表示从pos位置开始查找
		if (pos == string::npos)
		{
			break;
		}
		s[pos] = '0';
		pos += 1;
	}
	cout << s << endl;
}

rfind:

substr:substr(pos=0,n=npos) :从pos的位置 开始,截取n个字符  。如果n没有传递,表示从pos位置开始一直截取到末尾。如果pos和n都没传递,将字符串整个进行截取。

//需求:获取文件的后缀---->文件名中最后一个点之后的内容
void TestString18()
{
	string s("1234.txt.cpp");
	size_t pos = s.rfind('.')+1;
	
	string posFix = s.substr(pos);
	cout << posFix << endl;

}

 有多行单词,获取每行单词中最后一个单词的长度,每行中单词和单词之间以空格隔开

void GetLastWordLenght()
{
	string s;
	while (getline(cin,s))  //注意:cin以空包字符为分割的,空格,tab,回车    
		                    //genline(cin,s):获取一整行字符串 字符串中如果包含空格等空白字符也可以接收
	{
		size_t start = s.rfind(' ') + 1;
		string word = s.substr(start);
		cout << word.size() << endl;
	}
}

 


例题:

字符串相加:

给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。

你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

示例 1:

输入:num1 = "11", num2 = "123"
输出:"134"

示例 2:

输入:num1 = "456", num2 = "77"
输出:"533"

示例 3:

输入:num1 = "0", num2 = "0"
输出:"0"
class Solution
{
public:
	string addStrings(string num1, string num2)
	{
		//1.找出位数最多的
		int LSize = num1.size();
		int RSize = num2.size();
		if (RSize > LSize)
		{
			num1.swap(num2);
			swap(LSize, RSize);
		}

		//num1的位数  一定大于 num2的位数

		//2.设置保存结果的字符串
		string ret(LSize+1, '0');

		//模拟实现加法
		int step = 0;
		for (int idx1 = LSize - 1, idx2 = RSize - 1; idx1 >= 0; idx1--, idx2--)
		{
			//获取左操作数中的每一位
			int val = num1[idx1] - '0';   //字符就会变成数字

			//如果num2中有数字
			if (idx2 >= 0)
			{
				val += num2[idx2] - '0';
			}
			val += step;
			
			//考虑是否进位
			step = 0;
			if (val >= 10)
			{
				step = 1;
				val -= 10;
			}
			ret[idx1 + 1] += val;

		}

		if (step == 1)
			ret[0] += 1;
		else
			ret.erase(ret.begin());
		
		return ret;

	}
};

 

  • 40
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值