IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> STL -- String -> 正文阅读

[C++知识库]STL -- String

预学先知

1.STL的六大组件,容器,算法,迭代器最为重要,allocator就是个内存池在这里插入图片描述

2.STL是学习C++中一个很重要的方面,需要认真对待。“没有学过STL就不要说自己学过C++”; 三个境界:能用,明理,能扩展

3 .辅助学习的网站www.cplusplus.com

4.几个编码表
ascii – 表示英文编码表
unicode – 表示全世界文字编码表 utf-8 utf-16 utf-32
gbk --中文自己量身定做的编码表
Linux上一般设置utf-8
根据编码的不同,也有不同的方式进行存储
在这里插入图片描述

String类

1 .对应于C语言中的串,C++中式管理字符串的类, 同样以/0结尾,basic_string中每个字符用char存储

2 .构造方法中,重点掌握三个

在这里插入图片描述

#include<cstring>
#include<iostream>

using namespace std;

int main() {
	//无参
	string s1;
	//带参
	string s2("hello world");
	//拷贝构造
	string s3(s2);

	cin >> s1;
	cout << s1 << endl;
	cout << s2 << " " << s3 << endl;

	//其他的一些初始化方式
	string s4(s2, 0, 3); // 左闭右开的
	cout << s4 << endl;
	string s5(s2, 2);//npos 就是无限大,可以将整个字符串初始化完
	cout << s5 << endl;
	string s6("heiheihei", 5);
	cout << s6 << endl;
	string s7(10, '.');
	cout << s7 << endl;
	return 0;
}

3 .size和length

都可以用来计算字符串的有效字符(不包含最后的标识符’\0’),两者没有区别,但是size符合各种容器,一般我们用size (历史原因,size的意思是“个数”,适用性更广,比如说树就讲个数,而string既可以讲长度也可以讲个数,但是用出来的效果没有什么区别)
在这里插入图片描述

int main() {
	string s1("hello zkx");
	//统计的是有效字符的个数,不包含\0
	cout << s1.size() << endl;
	cout << s1.length() << endl;

	cout << s1 << endl;
	cout << s1.capacity() << endl;//统计的也是有效个数
	
	s1.clear();
	cout <<s1<< endl;
	cout << s1.capacity() << endl;//统计的也是有效个数

	return 0;
}

4 . [ ]方括号

可以让string像数组一样访问每个字符,at也可以访问每个字符,但是在遇到数组越界等异常的时候,[ ] 方括号是直接断言assert强制报错,at是进行异常处理,另外[ ]用的比较多
在这里插入图片描述

#include<cstring>
#include<iostream>

using namespace std;

int main() {
	string s1("hello zkx");
	for (int i = 0; i < s1.size(); i++) {
		cout << s1[i] << " ";
	}
	cout << endl;

	for (int i = 0; i < s1.size(); i++) {
		s1[i] += 1;
	}
	for (int i = 0; i < s1.size(); i++) {
		cout << s1[i] << " ";
	}
	cout << endl;


	return 0;
}

5 .附加字符方面,建议使用+

在这里插入图片描述

#include<cstring>
#include<iostream>

using namespace std;

int main() {
	string s1;
	s1.push_back('1');
	s1.append("213");
	s1 += "12312312";
	cout << s1 << endl;

	return 0;
}

6 . 遍历 + 修改 string 的三种方式以及反向迭代器的介绍

#include<cstring>
#include<iostream>

using namespace std;

int main() {
	string s1("hello zkx");
	
	//遍历的三种方法(也都可以队数据进行修改)
	//方法一:for + [ ]
	for (int i = 0; i < s1.size(); i++) {
		cout << s1[i];
	}
	cout << endl;

	//方法二:迭代器的方式
	//暂且可以把迭代器理解成一个指针,通过*也可以对数据进行修改
	string::iterator it = s1.begin();
	while (it != s1.end()) {
		*it += 1;
		++it;
	}
	cout << endl;

	it = s1.begin();
	while (it != s1.end()) {
		cout << *it << " ";
		++it;
	}
	cout << endl;
	
	//方法三:范围for/语法糖(其实本质也是迭代器,只不过写起来简单很多)
	for (auto& e : s1) {
		e -= 1;
	}
	for (auto e : s1) {
		cout << e;
	}
	cout << endl;
	
	//反向迭代器
	//string::reverse_iterator rit = s1.rbegin();
	auto rit = s1.rbegin();
	while (rit != s1.rend()) {
		cout << *rit;
		rit++;
	}
	cout << endl;
	return 0;
}

迭代器的意义是说明?
所有的容器都可以使用迭代器这种方式去访问修改
对于string,无论是正着倒着,下标+[ ] 的方式都很好用
但是如果是其他的数据结构(不支持下标+[ ]),那么迭代器的用处就体现出来了
结论:对于string来说,要学会使用迭代器,但是一般我们还是喜欢用下标+[ ] 的方式

注意:!=是通用的, 虽然<没有重载也可以使用
用了模板以后报错可能会特别长,不用害怕,看前几行就行
带const的迭代器不能被修改

7 . reserve 和 resize

一个是开空间 ,一个是开空间加初始化
如果本来字符串有值,reserve和resize都不会对其造成影响,resize会往后面追加数据,但是如果resize的字符量小于原来有的字符量,最后以小的字符量为准
在这里插入图片描述

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

int main() {
	//reserve初始化100个空间
	string s1;
	s1.reserve(100);
	
	//resize初始化100个空间,并将这些空间都赋值成1
	string s2;
	s2.resize(100, '1');

	string s3("hello world");
	s3.reserve(100);

	string s4("hello world");
	s4.resize(100, 'a');

	string s5("hello world");
	s5.resize(5);
	return 0;
}

8 .c_str 可以让c++中的string使用C语言中的一些函数,它的存在是有意义的
在这里插入图片描述

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

int main() {
	string s1("hello world");
	//两种输出的结果是类似的,但是原理缺不同
	//第一种是string重载了流的运算符进行输出
	//第二种是返回了const char* 的指针,c语言的方式进行输出
	cout << s1 << endl;
	cout << s1.c_str() << endl;
	

	return 0;
}

9 .find和rfind

rfind是find倒着找

在这里插入图片描述
nops给了一个定值-1,其实是一个无线大的数
在这里插入图片描述
substr是左闭右开的
在这里插入图片描述

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

int main() {
	string s1("hello world");
	//两种输出的结果是类似的,但是原理缺不同
	//第一种是string重载了流的运算符进行输出
	//第二种是返回了const char* 的指针,c语言的方式进行输出
	cout << s1 << endl;
	cout << s1.c_str() << endl;
	
	string file("test.txt");
	FILE* fout = fopen(file.c_str(), "w");

	//要求取出文件的后缀名
	size_t pos = file.find('.');
	if (pos != string::npos) {
		string suffix = file.substr(pos, file.size()-pos);
		cout << suffix << endl;
	}

	return 0;
}

应用:分离一个网址的协议,域名,地址

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;


int main() {
	//http://cplusplus.com/reference/string/string/?kw=string
	//取出这个网址url的协议protocol,域名domain,地址uri
	//substr是左闭右开的
	string url = "http://cplusplus.com/reference/string/string/?kw=string";
	size_t pos1 = url.find(':');
	string protocol = url.substr(0, pos1 - 0);//substr不会对原串造成影响
	cout << protocol << endl;
	size_t pos2 = url.find('/', pos1 + 3);//:后面三个字符找到c开始后面的第一个/
	string domain = url.substr(pos1 + 3, pos2);//substr左闭右开
	cout << domain << endl;
	string uri = url.substr(pos2 + 1);//后面无参默认一只往后
	cout << uri << endl;


	return 0;
}

10 .erase和insert

总的来说,插入和删除,都不建议使用,尾增和尾增还可以,但头插和任意位置删效率很低,O(n)的时间复杂度
在这里插入图片描述
在这里插入图片描述

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

int main() {
	//insert操作,头插的复杂度很高,不建议
	string s1("hello world");
	s1 += "1";
	s1 += "2312312";
	cout << endl;
	
	s1.insert(0, 1, 'x');//头插一个'x'
	s1.insert(s1.begin(), 'y');//迭代器,头插一个'y'
	s1.insert(0, "test");//头插一个字符串
	cout << s1 << endl;

	//erase操作
	string s2("hello world");
	s2.erase(0, 1);//头删一个数
	s2.erase(s2.size() - 1, 1);//尾删一个数
	cout << s2 << endl;
	return 0;
}

11 .string类型转换

头文件#include< string >才可以是哟个c++11特有的
在这里插入图片描述

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

int main() {
	//c++11的用法
	string s1 = "123";
	int i1 = stoi("123");
	int i2 = stoi(s1);
	cout << i1 << " " << i2 << endl;

	string s2 = to_string(12334);
	cout << s2 << endl;

	return 0;
}

12 .string类的模拟实现

传统写法
头文件
string.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<iostream>
#include<cstring>
#include<assert.h>

using namespace std;

namespace zkx {
	class string {
	public:
		//提供一个无参的
		//string()
		//	:_str(new char[1])
		//	, _size(0)
		//	, _capacity(0)
		//{
		//	_str[0] = '\0';//c_str输出时候遇到\0停止
		//}
		typedef char* iterator;
		typedef const char* const_iterator;

		iterator begin()
		{
			return _str;
		}

		const_iterator begin()const
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		const_iterator end()const
		{
			return _str + _size;
		}

		string(const char* str = "")//常量字符串默认有\0
			:_size(strlen(str))
			,_capacity(_size)//在堆上开辟空间,可以进行修改
		{
			_str = new char[_capacity+1];
			strcpy(_str, str);
		}

		string(const string& str)
			: _size(str._size)
			,_capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str._str);
		}

		string& operator=(const string& s)
		{
			/*if (this != &s)
			{
				delete[] _str;
				_str = new char[strlen(s._str) + 1];
				strcpy(_str, s._str);
			}*/

			if (this != &s)
			{	//防止开空间失败,先开空间再释放,这样开空间失败以后也不会执行后面的delete
				char* temp = new char[strlen(s._str) + 1];
				strcpy(temp, s._str);
				delete[] _str;
				_str = temp;
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}

		~string()
		{
			delete[] _str;
			_str = nullptr;
			_capacity = 0;
			_size = 0;
		}

		//遇到\0会停止
		const char* c_str()
		{
			return _str;
		}

		size_t size() const
		{
			return _size;
		}

		char& operator[](size_t pos)
		{
			assert(pos < _size);//判断越界访问
			return _str[pos];
		}

		const char& operator[](size_t pos) const 
		{
			assert(pos < _size);
			return _str[pos];
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}

test.cpp

#include"string.h"

void test1()
{
	zkx::string s1("zkxxxxx");
	zkx::string s2(s1);
	zkx::string s3("hello");
	s1 = s3;

	zkx::string s;
}

void test2()
{
	zkx::string s("zkx123123");
	s[0] = 'k';
	for (int i = 0; i < s.size(); i++)
	{
		cout << s[i];
	}
	cout << endl;

	zkx::string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}

	zkx::string::const_iterator it1 = s.begin();
	while (it1 != s.end())
	{
		cout << *it1 << " ";
		it1++;
	}
	
	//实际上就被替换成了上面的一段代码
	for (auto e : s)
	{
		cout << e << " ";
	}
}

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

现代写法完整版

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdio>
#include<iostream>
#include<cstring>
#include<assert.h>
#include<algorithm>

using namespace std;

//实现一个简单的string,不涉及增删查改
namespace zkx{
	
	class string {
	public:
		typedef char* iterator;
		typedef const char* const_iterator;

		iterator begin()
		{
			return _str;
		}
		 
		const_iterator begin()const
		{
			return _str;
		}

		iterator end()
		{
			return _str+_size;
		}

		const_iterator end()const
		{
			return _str + _size;
		}

		string(const char* str = "")
			:_size(strlen(str))
			,_capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		//s2(s1)
		string(const string& s2)
			:_str(nullptr)
			,_size(s2._size)
			,_capacity(s2._capacity)
		{
			string temp(s2._str);
			swap(temp);
		}

		void swap(string& s2)
		{
			std::swap(_str, s2._str);
			std::swap(_size, s2._size);
			std::swap(_capacity, s2._capacity);
		}

		//s1 = s2
		string& operator=(string s2)
		{
			swap(s2);
			return *this;
		}

		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}

		const char& operator[](size_t pos)const 
		{
			assert(pos < _size);
			return _str[pos];
		}

		const int size()const
		{
			return _size;
		}

		const char* c_str()const 
		{
			return _str;
		}

		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* temp = new char[n+1];//注意这边的n的有效容量的大小,+1是为了放\0
				strcpy(temp, _str);
				delete[] _str;
				_str = temp;
				_capacity = n;
			}
		}

		void append(const char* s)
		{
			//int len = strlen(s);
			//if (len + _size > _capacity)
			//{
			//	reserve(len + _size);//开刚刚好的空间
			//}
			//strcpy(_str + _size, s);
			//_size += len;
			insert(_size, s);
		}

		void push_back(char ch)
		{
			/*if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2*_capacity);
			}

			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';
			_capacity *= 2;*/

			insert(_size, ch);
		}


		string& operator+=(const char* s)
		{
			append(s);
			return *this;
		}

		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		void resize(size_t n, char ch = '\0')
		{
			if (n <= _capacity)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				if (n > _capacity)
				{
					reserve(n);
				}

				memset(_str + _size, ch, n - _size);
				_size = n;
				_str[n] = '\0';
			}
		}

		size_t find(char ch)
		{
			for (int i = 0; i < _size; i++)
			{
				if (_str[i] == ch)
					return i;
			}

			return npos;
		}
		//字串查找的算法
		size_t find(const char* s, size_t pos = 0)
		{
			const char* ptr = strstr(_str + pos, s);
			if (ptr == nullptr)//
			{	//没找到
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
				//_capacity *= 2;
			}
			size_t end = _size + 1;
			while (end > pos )
			{
				_str[end] = _str[end - 1];
				end--;
			}
			_str[end] = ch;
			++_size;
			return *this;
		}


		string& insert(size_t pos,const char* s)
		{
			assert(pos < _size);
			
			size_t len = strlen(s);
			if (_size + len > _capacity)
			{
				reserve(_size + len+1);
			}
			size_t end = _size + len;

			while (end>= pos + len)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, s, len);
			_size += len;

			return *this;
		}

		string& erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);
			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
			return *this;
		}
		/*ostream& operator<<(ostream& out)
	{
		for (auto e : *this)
		{
			out << e;
		}
		return out;
	}*/
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}

		/*bool operator>(string& s)
		{
			strcmp(_str, s._str);
		}*/
	private:
		char* _str;
		size_t _size;
		size_t _capacity;//存储有效字符的个数,不包含\0

		static const size_t npos = -1;
	};

	bool operator>(const string& s1, const string& s2)
	{
		/*size_t i1 = 0, i2 = 0;
		while (i1 < s1.size() && i2 < s2.size())
		{
			if (s1[i1] > s2[i2])
			{
				return true;
			}
			else if (s1[i1] < s2[i2])
			{
				return false;
			}
			else
			{
				i1++, i2++;
			}
			return i2 < s2.size() ? true : false;
		}*/
		
		//偷懒写法
		return strcmp(s1.c_str(), s2.c_str()) >0;
	}

	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}

	bool operator<(const string& s1, const string& s2)
	{
		return !((s1 == s2) || (s1 > s2));
	}

	bool operator<=(const string& s1, const string& s2)
	{
		return (s1 < s2) || (s1 == s2);
	}

	bool operator>=(const string& s1, const string& s2)
	{
		return (s1 > s2) || (s1 == s2);

	}

	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}

	ostream& operator<<(ostream& out ,const string& s)
	{
		for (auto e : s)
		{
			out << e;
		}
		return out;
	}

	istream& operator>>(istream& in, string& s)
	{
		char ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			s += ch;
			ch = in.get();
		}
		return in;
	}

	void test()
	{
		//string s1("hello world");
		//string s3("zkx");
		//s1 = s3;
		//s3.append("ahhahahahhahaha");
		//cout << s3.c_str()<< endl;

		//string s2;
		//s2.push_back('c');
		//s2.append("sjakjskasja");
		s2.insert(0, 'l');
		//cout << s2.c_str() << endl;
		//s2.insert(0, 'h');
		//cout << s2.c_str() << endl;
		string s1("zkxx");
		s1.insert(0,"haha");
		cout << s1.c_str() << endl;

	}
	void test2()
	{
		string s2;
		cin >> s2;

		cout << s2;
	}
};





  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-30 18:05:26  更:2022-03-30 18:06:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:37:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码