C++:string

1.STL简介

STL(standard template library标准模版库),是c++标准库的重要组成部分,是一个包罗数据结构与算法的软件框架

STL有很多版本,我们学习STL要阅读部分源代码,主要参考SGI版本。

STL的六大组件

网上有句话说:“不懂STL,不要说你会C++”。STL是C++中的优秀作品,有了它的陪伴,许多底层 的数据结构以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。

2.string类

string属于c++标准库,诞生比STL早,在广义的角度,string也属于容器。

在string内种有一百多种函数,我们只需要掌握重要的就行了。

学习这部分知识我们需要学会查字典

https://cplusplus.com

使用string类要包头文件#inlucde<string>,还有using namespace std;

 constructor(构造):

string s1;//空的
string s2("hello world");
string s3(s2);
cout << s1 << endl;//
cout << s2 << endl;//hello world
cout << s3 << endl;//hello world
//string类中有流插入和流提取的函数

 第三个是在给定的类中的第pos位置,开始复制npos数个。

string s4(s2, 6, 5);//world
//如果len的长度大于后面字符的长度,只复制到字符串结束

第五个是给定的字符串,复制n个。

string s6("hello world", 5);//hello

第六个是给类构造n个字符、

string s7(10, 'c');
cout << s7<< endl;//cccccccccc

 

这个函数的目的就是能修改对象的字符串中的字符。

s6[0] = 'x';
cout << s6;//xello

 三种方法遍历string类的对象:

//下标+[]
string s("hello world");
for(size_t i=0;i<s.size();i++)
{
    cout<<s[i];
}
cout<<endl;
//迭代器
string::iterator it=s.begin();//iterator像指针的东西
while(it!=s.end())//s.end()指向最后一个位置的下一个位置
{
    //使用迭代器也可修改
    //*it+=2;
    cout<<*it;//*是运算符重载
    it++;
}
//任何容器都可以用迭代器来访问
//范围for     底层就是迭代器
for(auto ch:s);//auto在c++中是自动推导。自动赋值,自动迭代,自动判断
{
    ch+=2;//打印后是每个字符加2的结果,但s没变,还是原来的字符。
    //要想变的话,在auto后面加&
    cout<<ch;
}
cout<<endl;
auto:

这不是string里面的用法,这里来介绍一下auto的用法.

  • 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
  • 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际 只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
  • auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
  • auto不能直接用来声明数组
int func()
{
    return 10;
}
int main()
{
    int a = 10;
    auto b=a;
    auto c = 'a';
    auto d = func();
    //"auto"的符号必须具有初始值设定项
    //auto e;
    cout << typeid(a).name() << endl;
    cout << typeid(c).name() << endl;
    cout << typeid(d).name() << endl; 
    //范围for适用于容器和数组
    int array[] = { 1,2,3,4,5 };
    //C++98的遍历
    for (int i = 0; i < sizeof(array) / sizeof(int); i++)
    {
	    array[i] *= 2;
    }
    for (int i = 0; i < sizeof(array) / sizeof(int); i++)
    {
	    cout << array[i] << endl;
    }
    //C++
    for (auto& e : array)
	    e *= 2;
    for (auto& e : array)
	    cout << e << " " << endl;
}

auto可以缩短代码,比如:

    map<string, string> dict;
	//map<string, string>::iterator mit = dict.begin();
	auto mit = dict.begin();
iterator:

begin和end在上面已经见识到了,那我们来看看rbegin,rend。

//反向迭代器
string::reverse_iterator rit = s1.rbegin();//rbegin指向最后一个字符
while (rit != s1.rend())//rend指向第一个字符的前一个
{
	cout << *rit << " ";//d l r o w   o l l e h
	++rit;
}
cout << endl;

反过来遍历。

 看到下面还有const_iterator begin() const,这里是让对象只可读不可改,所以我们迭代器有四种类型。

const string s2("hello world");
 string::const_iterator cit = s2.begin();
while (cit != s2.end())
{
	cout << *cit << " ";
	++cit;
}
string::const_reverse_iterator crit = s2.rbegin();
//auto crit = s2.rbegin();
while (crit != s2.rend())
{
	cout << *crit << " ";
	++crit;
}

 cbegin,cend返回的是被const修饰的迭代器,指向字符串,不能修改。只是与对象被const的作区分。

capacity:

size()与length()的效果一样,但length还是有局限性。一般都用size()。

max_size()是string对象所能达到的最大的长度。

resize()是改变对象的长度。对象的容量不变。

 第二个用法就是长度变长后,字符c填充到新的空间。

string str("I like to code in C");
cout << str << '\n';

unsigned sz = str.size();

str.resize(sz + 2, '+');
cout << str << '\n';//I like to code in C++

str.resize(14);
cout << str << '\n';//I like to code

 capacity(),容量,在vs上长度小于等于15时,容量为15。这是因为string底层有一个数组buf[16],最后一个存\0,如果数据长度小于15,存在buf上,不存在str上,大于15时,存在str开辟的堆空间上,buf就不存数据。如果在对象后面追加字符,容量会自动扩容。

class string
{

private:
    char buf[16];
    char*_str;
    int _size;
    int _capacity;
};

 reserve(),是给容量开好空间(可以提前开),但空间大小大于等于给的数据。假如开的过多,容量不会缩容。假如开的不够,容量会自动扩。

clear(),清除数据,但不清除容量。

empty()是来判断字符串是否为常量。

 shrink_to_fit(),是用来缩容到合适的大小,避免空间浪费。

 在vs上调试能看到这些东西:

Modifiers:

operator+=,append,push_back都是尾插,一般用的最多的是+=。

push_back只能尾插字符

int main()
{
    string s("hello world");
    s.push_back(' ');
    s.push_back('#');
    cout<<s<<endl;//hello world #
    return 0;
}

 

append可以尾插字符串,这些如果要用到,查一下就好了。

int main()
{
    string s("hello world");
    s.push_back(' ');
    s.push_back('#');
    cout<<s<<endl;//hello world #
    
    s.append("xyz");
    cout<<s<<endl;//hello world #xyz
    return 0;
}

 

int main()
{
    string s("hello world");
    s.push_back(' ');
    s.push_back('#');
    cout<<s<<endl;//hello world #
    
    s.append("xyz");
    cout<<s<<endl;//hello world #xyz
    s+='$';
    cout<<s<<endl;//hello world #xyz$
    return 0;
}

 insert插入,在指定位置插入字符或字符串

erase删除,删除一部分字符串,来减小长度。

string s("hello world");
s.erase(0, 1);//头删
cout << s << endl;//ello world
s.erase(s.begin());llo world
cout << s << endl;
//尾删
s.erase(--s.end());
cout << s << endl;
s.erase(s.size() - 1, 1);
cout << s << endl;
string ss("hello world");
ss.erase(6);//hello

 replace,将字符串的字符替换成字符或字符串。

string sss("hello world hello world");
size_t pos = sss.find(' ');
while (pos != string::npos)
{
	sss.replace(pos, 1, "%%");
	pos = sss.find(' ',pos+2);
}
cout << sss << endl;//hello%%world%%hello%%world

swap(),交换两个对象的内容。

String operations:

c_str(),就是获得对象的_str(字符串)。

find,就是找字符或字符串,返回下标,rfind就是倒着找。

substr就是生成一个子字符串

string s("test.cpp.zip");
size_t pos = s.rfind('.');
string suffix = s.substr(pos);
cout << suffix << endl;//.zip
 Member constants:

npos:

 当在字符串的成员函数中用作 len(或 sublen)参数的值时,此值表示“直到字符串的末尾”。

作为返回值,它通常用于指示不匹配。

此常量使用值 -1 定义,由于size_t是无符号整数类型,因此它是此类型的最大可能可表示值。

 getline:

第一种是,输入字符,直到遇到字符delim为止,第二种是'\n'表示终止,可以识别空格。

3.模拟实现string底层

string.h:
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;
namespace byh
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str+_size;
		}
		const_iterator begin()const
		{ 
			return _str;
		}
		const_iterator end()const
		{
			return _str+_size;
		}

		/*string()
			:_str(new char[1] {'\0'})
			, _size(0)
			, _capacity(0)
		{}*/
		//短小频繁调用的函数,可以直接定义到类里面,默认是inline
		string(const char* str="")
		{
			_size = strlen(str);
			//_capacity不包含\0
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		//深拷贝问题
		string(const string& s)
		{
			_str = new char[s._capacity + 1];
			strcpy(_str, s._str);
			_size = s._size;
			_capacity = s._capacity;
		}
		string& operator=(const string& s)
		{
			if(this!=&s)
			{
				delete[]_str;
				_str = new char[s._capacity + 1];
				strcpy(_str, s._str);
				_size = s._size;
				_capacity = s._capacity;	
			}
			return *this;
		}
		~string()
		{
			delete[]_str;
			_str = nullptr;
			_size = _capacity = 0;
		}
		const char* c_str()const
		{
			return _str;
		}
		size_t size()const
		{
			return _size;
		}
		size_t capacity()const
		{
			return _capacity;
		}
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}
		const char& operator[](size_t pos)const
		{
			assert(pos < _size);
			return _str[pos];
		}

		void push_back(char ch);
		void append(const char* str);
		string& operator+=(char ch);
		string& operator+=(const char* str);
		void reserve(size_t n);

		void insert(size_t pos, char ch);
		void insert(size_t pos, const char* str);
		void erase(size_t pos, size_t len=npos);
		size_t find(char ch, size_t pos = 0);
		size_t find(const char* str, size_t pos=0);
		string substr(size_t pos = 0, size_t len = npos);
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
		
		static const size_t npos;
		//static const size_t npos=-1;这样也行
	};
	bool operator<(const string& s1, const string& s2);
	bool operator<=(const string& s1, const string& s2);
	bool operator>(const string& s1, const string& s2);
	bool operator>=(const string& s1, const string& s2);
	bool operator==(const string& s1, const string& s2);
	bool operator!=(const string& s1, const string& s2);
	ostream& operator<<(ostream& out, const string& s);
	istream& operator>>(istream& in, string& s);
}
string.cpp:
#include"string.h"
namespace byh
{
	const size_t string::npos = -1;
	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[]_str;
			_str = tmp;
			_capacity = n;
		}
	}
	void string::push_back(char ch)
	{
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}
		_str[_size] = ch;
		++_size;
		_str[_size] = '\0';
	}
	void string::append(const char* str)
	{
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			//大于二倍,需要多少开多少
			reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
		}
		strcpy(_str + _size, str);
		_size += len;
	}
	string& string::operator+=(char ch)
	{
		push_back(ch);
		return *this;
	}
	string& string::operator+=(const char* str)
	{
		append(str);
		return *this;
	}
	void string:: insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}
		//挪动数据
		/*	int end = _size;
		while (end>=(int)pos)
		{
			_str[end + 1] = _str[end];
			--end;
		}
		_str[pos] = ch;
		_size++;*/
		size_t end = _size + 1;
		while (end > pos)
		{
			_str[end] = _str[end - 1];
			--end;
		}
		_str[pos] = ch;
		_size++;
	}
	void string::insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		size_t len = strlen(str);
		if (len == 0)
			return;
		if (_size + len > _capacity)
		{
			reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);	
		}
		size_t end = _size + len;
		while(end>pos+len-1)
		{
			_str[end] = _str[end-len];
			end--;
		}
		for (size_t i = 0; i < len; i++)
		{
			_str[pos + i] = str[i];
		}
		_size += len; 
	}
	void string::erase(size_t pos, size_t len)
	{
		assert(pos < _size);
		if (len >= _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			for (size_t i = pos + len; i <= _size; i++)
			{
				_str[i - len] = _str[i];
			}
			_size -= len;
		}
	}
	size_t string::find(char ch, size_t pos)
	{
		for (size_t i = pos; i < _size; i++)
		{
			if (_str[i] == ch)
			{
				return i;
			}
		}
		return npos;
	}
	size_t string::find(const char* str, size_t pos)
	{
		assert(pos < _size);
		const char* ptr = strstr(_str + pos, str);
		if (ptr == nullptr)
		{
			return npos;
		}
		else
		{
			return ptr - _str;
		}
	}
	string string::substr(size_t pos, size_t len)
	{
		assert(pos < _size);
		//len大于剩余字符长度,更新一下len
		if (len > _size - pos)
		{
			len = _size - pos;
		}
		string sub;
		sub.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			sub += _str[pos + i];
		}
		return sub;
	}
	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);
	}
	bool operator>=(const string& s1, const string& s2)
	{
		return !(s1 < s2);
	}
	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);
	}
	ostream& operator<<(ostream& out, const string& s)
	{
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}
	istream& operator>>(istream& in, string& s)
	{
		s.clear();
		const int N = 256;
		char buff[N];
		int i = 0;
		char ch;
		ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == N - 1)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}
}
test.cpp:
#include"string.h"
using namespace std;

namespace byh
{
	void test_string1()
	{
		string s1;
		string s2("hello world");
		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;

		for (size_t i = 0; i < s2.size(); i++)
		{
			s2[i] += 2;
		}
		cout << s2.c_str() << endl;
		for (auto ch : s2)
		{
			cout << ch;
		}
		cout << endl;
		string::iterator it = s2.begin();
		while (it != s2.end())
		{
			//*it += 2;
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}
	void test_string2()
	{
		string s1("hello world");
		s1 += 'x';
		s1 += '#';
		cout << s1.c_str() << endl;
		s1 += "ye";
		cout << s1.c_str() << endl;
		s1.insert(0, '$');
		cout << s1.c_str() << endl;
		s1.insert(6, "$$$");
		cout << s1.c_str() << endl;
	}
	void test_string3()
	{
		string s1("hello world");
		s1.erase(6, 100);
		cout << s1.c_str() << endl;

		string s2("hello world");
		s2.erase(6);
		cout << s2.c_str() << endl;

		string s3("hello world");
		s3.erase(6, 3);
		cout << s3.c_str() << endl;
	}
	void test_string4()
	{
		string s1("test.cpp.zip");
		size_t pos = s1.find('.');
		string suffix = s1.substr(pos);
		cout << suffix.c_str() << endl;
	}
	void test_string5()
	{
		string s1("hello world");
		string s2("hello world");

		cout << (s1 < s2) << endl;
		cout << (s1 == s2) << endl;
		cout << ("hello world" == s2) << endl;
		cout << (s1 > "hello") << endl;
		//cout<<("hello"=="hello")<<endl;
		cout << s1 << s2 << endl;
		string s0;
		cin >> s0;
		cout << s0 << endl;
	}
}
int main()
{
	//byh::test_string1();
	byh::test_string5();
	return 0;
}

完,感谢观看。

C++的std::string是一个用于处理字符串的标准库类。它具有多种初始化方式,可以创建空字符串、包含指定内容的字符串等。例如,可以使用以下方式进行字符串的初始化: - 使用默认构造函数创建一个空字符串:std::string str; - 使用括号初始化语法创建一个包含指定内容的字符串:std::string str("hello"); - 使用赋值语句创建一个包含指定内容的字符串:std::string str = "world"; std::string还支持字符串的连接操作。可以使用加号运算符或者将字符串直接连接起来实现连接操作。例如: - 使用加号运算符连接两个字符串:std::string str1 = "hello"; std::string str2 = "world"; std::string str3 = str1 + ", " + str2; // str3 等于 "hello, world" 此外,std::string还提供了一些用于字符串处理的函数和方法。例如,可以使用remove_if函数结合迭代器来移除字符串中的特定字符或字符范围。例如: - 使用std::remove_if函数移除字符串中的空格字符: std::string name = "John Doe"; std::string::iterator newend = std::remove_if(name.begin(), name.end(), iswhitespace); 此处的iswhitespace是一个判断字符是否为空格的谓词函数。 总结起来,C++的std::string类提供了多种初始化方式、字符串连接操作以及一些用于字符串处理的函数和方法。通过这些功能,我们可以方便地进行字符串的操作和处理。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [C++ 基础: std::string](https://blog.csdn.net/weixin_39568531/article/details/129657195)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [C++ 标准程序库std::string 详解](https://blog.csdn.net/CHYabc123456hh/article/details/108879371)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值