• 【C++】string类超详细解析


    参考文献:C++标准库官网
    前言:在C/C++的学习过程当中一定一定要多刷题,牛客网作为国内内容超级丰富的IT题库,尤其是它的C、C++,有从入门到大厂真题,而且大部分的考试题目也是从中抽取,还有很多面经,推荐大家去牛客网进行刷题练习,点击链接牛客网刷题入口


    前言

    在这里插入图片描述

    string是C++标准库的一个重要组成部分,主要用于字符串处理。在使用string类是必须包含#include以及using namespace std。

    我们从C++标准库的官网中可以知道,basic_string是string类的类模板,那为什么string需要类模板呢?因为在类的实例化中我们可以看到字符串有也有多种类型。这时候有小伙伴会疑问:字符串也有其它类型嘛?答案是:对的,是的。ASCII是最常用的编码,它是最通用的信息交换标准,一个字节8个比特位,最多只能表示256种符号,肯定是不够的,有时就需要多个字节来表示一个符号,比如简体中文常见的编码方式是GBK/GB2312,即使用两个字节表示一个汉字,并且同音字一般挨在一块。由于汉字太多了,包括繁体简体等等,所以才增加了utf-16,utf-32等字符串类型,这样可以扩大文字的数量和种类,这就是为什么string需要类模板的原因,我们在哪种环境或者情况下需要使用字符串,就定义哪种类型的字符串,比如在utf-16的环境下,我们就用U16string。

    总结: string类是basic_string模板类的一个实例化结果,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数。这里简单展示一下string的类模板定义。

    //动态增长字符数组
    template<class T>
    class basic_string
    {
    public:
    	//函数
    private:
    	T* _str;
    	size_t _size;
    	size_t _capacity;
    };
    typedef basic_string<char> string;//以char显示实例化basic_string模板类
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    一.string类对象常见的构造函数

    在这里插入图片描述
    我们现在来使用一下这些常见的字符串构造函数:

    #include
    #include
    using namespace std;
    int main()
    {
    	//default (1)string();构造空字符串
    	string s1;
    	cout << s1 << endl;//并且支持流插入和流提取
    
    	//copy (2)string(const char* str);
    	string s2("诶瑞巴蒂");
    	cout << s2 << endl;//输出诶瑞巴蒂
    	//追加
    	s2 += "嗨起来!";
    	cout << s2 << endl;//输出诶瑞巴蒂嗨起来!
    
    
    	//存在隐式类型转换,编译器会优化
    	//先构造string tmp("诶瑞巴蒂");
    	//再拷贝给string s3
    	string s3 = "诶瑞巴蒂";
    	cout << s3 << endl;//输出诶瑞巴蒂
    
    	//string(size_t n,char c)
    	string s4(10, '*');
    	cout << s4 << endl;//**********
    
    	//副本:拷贝构造函数
    	//string(const string& str)
    	string s5(s2);
    	string s6 = s2;
    	cout << s5 << s6 << endl;//输出诶瑞巴蒂嗨起来!
    
    	string s7("hello world", 5);
    	cout << s7 << endl;
    
    	string s10(s7, 2);
    	cout << s10 << endl;//llo
    
    
    	string s8(s7, 2, 3);
    	cout << s8 << endl;//llo
    
    	string s9(s7, 2, 20);
    	cout << s9 << endl;//llo
    
    	string s11(s7, 2, 8);
    	cout << s11 << endl;//llo
    
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

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

    访问(遍历)string类对象的内容,主要有三种操作方法:
    (1)下标[ ] ;
    (2)for+下标 或者 范围for(C++11才支持);
    (3)迭代器,分为正向迭代器,反向迭代器,const正向,const反向迭代器;

    1.operator下标[ ]

    在自定义类中,方括号[ ]也是需要我们通过运算符重载自己实现的,很好的是string类内部已经对[]运算符进行了重载并且该重载使用的是引用返回,所以可以通过[ ]+下标对元素进行修改,我们先来简单看一下下标的底层实现。

    char& operator[](size_t i)
    {
    	assert(i < _size);//防止越界访问
    	return _str[i];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    现在再来看看如何用下标遍历string类对象,s1可以直接调用关于string类对象容量操作的函数s1.size(),可以得到s1的长度。注意:下列代码的长度为4,size计算长度时不包括’\0’,类似strlen函数。

    #include
    #include
    using namespace std;
    int main()
    {
    	string s1("1234");
    
    	for (size_t i = 0; i < s1.size(); ++i)//0-4
    	{
    		s1[i]++;
    	}
    	cout << s1 << endl;//2345
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.范围for(C++11支持)

    若需要通过范围for修改对象的元素,则用于接收元素的变量e的类型必须是引用类型,否则e只是对象元素的拷贝(类似形参),对e的修改不会影响对象的元素。

    #include
    #include
    using namespace std;
    int main()
    {
    	string s1("1234");
    
    	for (char& e : s1)//如果需要修改,必须加&引用,这里也可用auto& e
    	{
    		e++;
    	}
    	cout << s1 << endl;//2345
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3.迭代器

    先来看看C++标准库中有关迭代器的函数接口,这里我们重点学习begin,end,rbegin,rend。
    在这里插入图片描述

    正向迭代和反向迭代:正向迭代器使用end和begin函数接口,反向迭代器使用rend和rbegin函数接口。

    #include
    #include
    using namespace std;
    int main()
    {
    	string s1("12345");
    
    	//正向迭代器
    	string::iterator it1 = s1.begin();
    	while (it1 != s1.end())
    	{
    		*it1 += 1;//可以把it1看成指针,对其内容进行+1
    		++it1;//然后指针++
    	}
    	it1 = s1.begin();
    	while (it1 != s1.end())
    	{
    		cout << *it1 << " ";//2 3 4 5 6
    		++it1;
    	}
    	cout << endl;
    
    	//反向迭代器
    	//string::reverse_iterator rit = s1.rbegin();
    	auto rit = s1.rbegin();//觉得类型名太长可以用auto
    
    	while (rit != s1.rend())
    	{
    		cout << *rit << " ";//6 5 4 3 2
    		++rit;
    	}
    	cout << endl;
    	return 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    const正向,const反向迭代器:只支持读数据,不能修改容器数据

    //const正反向:只支持读,不能修改容器数据
    #include
    #include
    using namespace std;
    int main()
    {
    	string s1("12345");
    
    	string::const_iterator it = s1.begin();
    	while (it != s1.end())
    	{
    		//*it += 1;//error
    		cout << *it << " ";//1 2 3 4 5
    		++it;
    	}
    	cout << endl;
    
    	auto rit = s1.rbegin();
    	while (rit != s1.rend())
    	{
    		cout << *rit << " ";//5 4 3 2 1
    		++rit;
    	}
    	cout << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    三.string类对象的容量操作

    1.基本:size,capacity,clear,empty

    在这里插入图片描述

    1.使用size函数或length函数获取当前有效字符的个数(不包括’\0’);
    2.使用max_size函数获取字符串可以到达的最大长度,这个值一般非常大,而且不变;
    3.使用capacity函数获取当前对象所分配的存储空间大小;
    4.使用clear函数删除对象的内容,清除之后对象变为空字符串,注意clear函数不释放空间,只是删除内容;
    5.使用empty函数判断对象是否为空,空返回1,不为空返回0;
    6.下面着重学习resize和reserve函数的使用。

    在这里插入图片描述

    2.reserve函数

    函数功能:void reserve(size_t n);按要求更改字符串容量。

    1.当n大于对象当前的capacity时,将capacity扩大到n,或者大于n(VS编译器下一般会比n大,大概以1.5倍的形式增长,而在LInux下,一般扩容后的空间是原来的两倍。)
    2.当n小于对象当前的capacity时,不会缩容,保持原样
    3.如果我们自己指定的空间不够,系统会自动扩容。

    使用reserve前:
    在这里插入图片描述
    使用reserve后:如果我们想一次性扩容:
    在这里插入图片描述

    3.resize函数

    在这里插入图片描述

    函数功能:设置字符串的长度大小。
    如最开始“hello world”的长度是11,当前对象的容量是15。则:
    n<11,即相当于删除数据
    11 n>15相当于扩容+插入数据

    #include
    #include
    using namespace std;
    int main()
    {
    	string s1("hello world");
    	s1.resize(5);
    	cout << s1.size() << endl;//5
    	cout << s1.length() << endl;//5
    	cout << s1.capacity() << endl;//15
    	cout << s1 << endl << endl;//hello
    
    	string s2("hello world");
    	s2.resize(15, 'x');
    	cout << s2.size() << endl;//15
    	cout << s2.length() << endl;//15
    	cout << s2.capacity() << endl;//15
    	cout << s2 << endl << endl;//hello worldxxxx
    
    	string s3("hello world");
    	s3.resize(20, 'x');
    	cout << s3.size() << endl;//20
    	cout << s3.length() << endl;//20
    	cout << s3.capacity() << endl;//31
    	cout << s3 << endl << endl;//hello worldxxxxxxxxx
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    四.string类对象的修改操作

    在这里插入图片描述

    1.operator+=

    C++标准库的运算符重载函数声明:
    在这里插入图片描述

    //string类对象的修改
    #include
    #include
    int main()
    {
    	std::string name("John");
    	std::string family("Smith");
    	name += " K. ";//c-string
    	name += family;//string
    	name += '!';//char c
    
    	std::cout << name;//John K. Smith!
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.append

    功能与+=相似

    #include 
    #include 
    int main()
    {
    	std::string str;
    	std::string str2 = "Writing ";
    	std::string str3 = "print 10 and then 5 more";
    
    	// used in the same order as described above:
    	str.append(str2);                       // "Writing "
    	str.append(str3, 6, 3);                   // "10 "
    	str.append("dots are cool", 5);          // "dots "
    	str.append("here: ");                   // "here: "
    	str.append(10u, '.');                    // ".........."
    	str.append(str3.begin() + 8, str3.end());  // " and then 5 more"
    
    	std::cout << str << '\n';
    	//Writing 10 dots here: .......... and then 5 more
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3.push_back尾插

    #include 
    #include 
    using namespace std;
    int main()
    {
    	string s;
    	s.push_back('C');
    	s.push_back('S');
    	s.push_back('D');
    	s.push_back('N');
    	cout << s << endl; //CSDN
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4.insert与erase插入与删除

    #include 
    #include 
    using namespace std;
    int main()
    {
    	string s("C"); //C
    
    	//insert(pos, str)在pos位置插入字符串str
    	s.insert(1, "S"); //CS
    
    	//insert(pos, string)在pos位置插入string对象
    	string t("D");
    	s.insert(2, t); //CSD
    
    	//insert(pos, char)在pos位置插入字符char
    	s.insert(s.end(), 'N'); //CSDN
    	
    	cout << s << endl; //CSDN
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    #include 
    #include 
    using namespace std;
    int main()
    {
    	string s("I like C++!!!");
    
    	//erase(pos, n)删除pos位置开始的n个字符
    	s.erase(8, 5); //I like C
    
    	//erase(pos)删除pos位置的字符
    	s.erase(s.end()-1); //I like
    
    	//erase(pos1, pos2)删除[pos1pos2)上所有字符
    	s.erase(s.begin() + 1, s.end()); //I
    
    	cout << s << endl; //I
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    5.assign与replace

    #include 
    #include 
    using namespace std;
    int main()
    {
    	//assign
    	string s1("hello world hello world");
    	string s2("hello world hello world");
    
    	s1.assign("hello zxn", 5);//赋值string的前5个字符
    	cout << s1 << endl;//hello
    
    	//从第6个字符开始,将5个字符都替换成"zxn"
    	s2.replace(6, 5, "zxn");
    	cout << s2 << endl; //hello zxn hello world
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    6.compare字符串比较

    注意比较的是ASCII码值。
    1.两个字符串相等,则返回0;
    2.比较字符串中第一个不匹配的字符值较小,或者所有比较字符都匹配,但比较字符串较短,则返回小于0的值。
    3.比较字符串中第一个不匹配的字符值较大,或者所有比较字符都匹配,但比较字符串较长,则返回大于0的值。

    #include 
    #include 
    using namespace std;
    int main()
    {
    	string s1("hello world");
    	string s2("hello CSDN");
    
    	//"hello world"和"hello CSDN"比较
    	cout << s1.compare(s2) << endl; //1
    
    	//"ell"和"hello CSDN"比较
    	cout << s1.compare(1, 3, s2) << endl; //-1
    
    	//"hello"和"hello"比较
    	cout << s1.compare(0, 4, s2, 0, 4) << endl; //0
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    7.swap交换

    #include 
    #include 
    using namespace std;
    int main()
    {
    	string s1("hello");
    	string s2("CSDN");
    	
    	//使用string类的成员函数swap交换s1和s2
    	s1.swap(s2);
    	cout << s1 << endl; //CSDN
    	cout << s2 << endl; //hello
    
    	//使用非成员函数swap交换s1和s2
    	swap(s1, s2);
    	cout << s1 << endl; //hello
    	cout << s2 << endl; //CSDN
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    8.小问题:利用replace将空格都替换成%20涉及find和replace

    #include 
    #include 
    using namespace std;
    int main()
    {
    	string s1("hello world hello world");
    
    	//1.不建议这种算法,在数据挪动方面消耗了太多时间
    	size_t pos = s1.find(' ');
    	while (pos != string::npos)//string::npos
    	{
    		s1.replace(pos, 1, "20%");
    		pos = s1.find(' ', pos + 3);//pos当前指向2,跳过3个字符,继续找
    	}
    	cout << s1 << endl;
    
    	//2.以空间换时间
    	string ret;
    	ret.reserve(s1.size());
    	//范围for
    	for (auto ch : s1)
    	{
    		//不为' '就插入,是' '就插入%20
    		if (ch != ' ')
    		{
    			ret += ch;
    		}
    		else
    		{
    			ret += "%20";
    		}
    	}
    	cout << ret << endl;
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    9.如何获取一个文件名的后缀

    #include 
    #include 
    using namespace std;
    int main()
    {
    	string file;
    	cin >> file;
    
    	//想要直到文件名的后缀,要先找到'.'
    	//size_t pos = file.find('.');//正向找
    	size_t pos = file.rfind('.');//反向找
    
    	if (pos != string::npos)//pos在安全范围内
    	{
    		string suffix1 = file.substr(pos);
    		cout << suffix1 << endl;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    ArrayList分析1:循环、扩容、版本
    word模板内容替换
    LeetCode 每日一题 2023/10/2-2023/10/8
    信息学奥赛一本通:2050:【例5.20】字串包含
    2022年第十四届蓝桥杯Python选拔赛11月27日中高级组
    ESXI6.5安装教程
    Java开发中的工作流程和步骤
    Linux 用户层、内核层和MMU
    matlab画图中图
    Mysql数据类型
  • 原文地址:https://blog.csdn.net/weixin_63449996/article/details/127759381