C++初阶——string(字符数组),跟C语言中的繁琐设计say goodbye

前言:在日常的程序设计中,我们会经常使用到字符串。比如一个人的身份证号,家庭住址等,只能用字符串表示。在C语言中,我们经常使用字符数组来存储字符串,但是某些场景(比如插入,删除)下操作起来很繁琐。在C++中,底层设计了一个string类,将经常使用的功能封装在其中,使用起来非常简单,便捷。
本文主要介绍string的使用方法和几个常用函数,最后再列举几个经典例题。
我们使用string时经常会用到它封装的函数,这里推荐一个网站:C++ Reference。函数忘了怎么使用时可以参考该网站。

一.介绍

长话短说,不将它讲的太神秘:
string是用字符的顺序数组实现的类,用于管理字符数组。
我们平常怎么使用自定义的,就怎么使用string。

二.使用

1.创建一个字符数组存储字符串hello world:

格式:string 对象("字符或字符串")
1.形如string str2("hello world"),实际上就是调用string类的构造函数初始化对象str2.
2.string类中有流提取流插入运算符重载,可以直接输出或者输出string类型数据。

//C语言
char str1[] = "hello world";
//C++
string str2("hello world"); //构造函数
string str3("#");  //单个字符
cout << str2 << endl; //string类中有流提取流插入运算符重载,可以直接输出或者输出string类型数据。

控制台输出如下:
在这里插入图片描述

2.截取部分字符串

string类对此也进行了设计:第一个参数为截取string对象,第二个为需要截取部分的起始下标,第三个参数为截取字符个数(单位是字节);
在这里插入图片描述

如下代码:想截取s1对象中的world给给s2,w的下标为6(第二个参数),world有5个字符(第三个参数)

int main()
{
    
    
	string s1("hello world");
	string s2(s1, 6, 5); //截取s1对象中的world给给s2
	cout << s2 << endl;
}

控制台输出如下,可以看到截取成功了:
在这里插入图片描述

3.从键盘上输入

这里就可以体现string的优点:
使用C语言,我们需要考虑给字符数组开多大的空间,而string不用我们考虑这点,我们只管输入,编译器会自动帮我们适配合适的空间。

int main()
{
    
    
	string s1; //实例化一个对象s1
	cin >> s1; // 输入
	cout << s1 << endl; //打印
	return 0;
}

如下:前两行为从键盘上输入的,后两行为打印的。
在这里插入图片描述

三.常用重载运算符和函数

string函数设计的接口,感兴趣的可以去文章开头给的网站看看。下面某些函数只给出常用的接口。

1.+/+=

单从这个题目,就能让你感受到string的魅力!

请看题目:从键盘上输入两个字符串,要求将后一个字符串拼接到前一个字符串后面,输出拼接后的前一个字符串。(字符串长度不超过50字节)
从C语言角度考虑:首先我们要开两个不小于50字节的字符数组,之后简单一点的就是使用strcat函数拼接。但是strcat函数长久不用我忘掉了呢,还要再去翻看笔记回忆。

C++string类对+运算符进行了重载,这个+的本质就是尾插

int main()
{
    
    
	string s1, s2;
	cin >> s1 >> s2;
	s1 = s1 + s2;  //s1,s2顺序不能颠倒
	//s1 += s2; //使用+=也可以
	cout << s1 << endl;
	return 0;
}

控制台输出如下:其中第一行为输入的s1,第二行为输入的s2,第三行为拼接后的s1
在这里插入图片描述

2.size()

返回string对象的有效字符长度(不包括\0),相当于C语言的strlen。

int main()
{
    
    
	string s1("hello world");
	cout << s1.size() << endl;
	return 0;
}

控制台输出如下:
在这里插入图片描述

3.capacity()

返回string对象的容量
我们知道string会自动扩容,而它当然不是一个一个字节阔的,它是按照一定倍率扩容。

int main()
{
    
    
	string s1("hello world");
	cout << "size:" << s1.size() << endl;
	cout << "capacity:" << s1.capacity() << endl;
	return 0;
}

控制台输出如下:可以看到有效字符大小为11字节,容量为15字节。
在这里插入图片描述

4.reserve()

reserve——保留(不要把它当成reverse(逆置))
功能:请求容量的变化
价值:如果确定大概需要开多大的空间,可以提前开好,减少扩容,提高效率。(扩容是有代价的,特别是异地扩容,需要拷贝旧空间的数据移到新空间)

int main()
{
    
    
	string s1("hello world");
	cout << "size:" << s1.size() << endl;
	cout << "原capacity:" << s1.capacity() << endl;
	s1.reserve(100);
	cout << "新capacity:" << s1.capacity() << endl;
	return 0;
}

控制台输出如下:
在这里插入图片描述

5.[]运算符(遍历string)

string类对[]运算符进行了重载,使得我们可以借助[]像访问数组一样访问string。对于解决一些与字符串有关的题很有帮助。

以遍历string为例:

int main()
{
    
    
	string s1("hello world");
	for (size_t i = 0; i < s1.size(); i++) //运用size()函数
	{
    
    
		cout << s1[i] << " ";  //下标访问
	}
	return 0;
}

控制台输出如下:
在这里插入图片描述

6.push_back()(尾部插入一个字符)

在string对象尾部插入一个字符

int main()
{
    
    
	string s1("hello world");
	s1.push_back('x');
	cout << s1 << endl;
	return 0;
}

控制台输出如下:
在这里插入图片描述

7.append() (尾部插入一段字符串)

形式:append(字符串/string对象)
实际上设计的有些冗余,插入的话我们直接用+=就行了,何必再使用append函数呢?

int main()
{
    
    
	string s1("hello world");
	string s2("!!!");
	s1.append("xxx");
	s1.append(s2);
	cout << s1 << endl;
	return 0;
}

控制台输出如下:
在这里插入图片描述

8.insert() (头部插入)

插入一个字符:
1.常规做法:s.insert(起始位置下标,插入个数,要插入字符)
2.迭代器做法:s.insert(s.begin(),要插入字符)

int main()
{
    
    
	string s1("hello world");
	string s2("hello world");
	string s3("hello world");
	s1.insert(1,1,'x'); // 在h后插入一个x
	s2.insert(1, 2, 'x'); // 在h后插入两个x 
	s3.insert(s3.begin(), 'x'); // 头插x 
	cout << "s1 = " << s1 << endl;  //hxello world
	cout << "s2 = " << s2 << endl;  //hxxello world
	cout << "s3 = " << s3 << endl;  //xhello world
	return 0;
}

插入一段字符串:s.insert(起始位置下标,字符串)

int main()
{
    
    
	string s1("hello world");
	s1.insert(1,"xx");
	cout << s1 << endl; //hxxello world
	return 0;
}

四.迭代器

1.iterator 正向迭代器(重点)

函数名 作用(下面函数仅适用于正向迭代器!!!)
begin() 返回string开头的地址
end() 返回string最后一个有效字符的下一个位置的地址(一般都是\0

iterator是迭代器的意思,它封装在string类中(list,树等数据结构中都有迭代器),故而使用时要受string类域限制,要写成string::iterator形式。使用时可以将它想象成一个指针来理解。

借助迭代器遍历string:

int main()
{
    
    
	string s1("hello world");
	string::iterator str = s1.begin();
	//auto str = s1.begin(); 写成这样也可以,auto是类型指示符,可以根据begin推出正向迭代器
	while (str != s1.end())  
	{
    
    
		cout << *str << " ";
		++str;
	}
	cout << endl;
	return 0;
}

控制台输入如下:
在这里插入图片描述

在这里插入图片描述
上述代码借助begin()函数返回开始位置的迭代器,end()指向最后一个字符下一个位置(此处就是\0)。

问:可这样子不就是单纯的指针遍历吗,何必那么麻烦还使用迭代器?
答:迭代器的一个特点就是通用性,在vector,list,树等中都有迭代器。就以list举例,它是由一个一个的小结点组成的,物理空间中并不是连续的,使用结点指针++并不能找到它的下一个结点,但是像上述代码一样套上迭代器就可以遍历list。因此,迭代器遍历才是最主流的遍历方式。

2.reverse_iterator 反向迭代器

函数名 作用(下面函数仅适用于反向迭代器!!!)
rbegin() 返回string最后一个有效字符的下一个位置的地址(一般都是\0
rend() 返回string开头的地址

顾名思义,跟正向迭代器相反,reverse_iterator是反正使用的。

借助反向迭代器倒着遍历string:

int main()
{
    
    
	string s1("hello world");
	string::reverse_iterator str = s1.rbegin();
	//auto str = s1.rbegin(); 写成这样也可以,auto是类型指示符,可以根据rbegin推出反向迭代器
	while (str != s1.rend())
	{
    
    
		cout << *str << " ";
		++str;
	}
	cout << endl;
	return 0;
}

控制台输入如下,可以看到它是倒着遍历的:
在这里插入图片描述

补:const迭代器:const_iterator const_reverse_iterator
总计共有4种迭代器。

3.reverse函数(字符串逆置神器)

传入迭代器区间: reverse(s.begin(),s.end());

int main()
{
    
    
	string s1("hello world");
	reverse(s1.begin(), s1.end()); //传入迭代器区间
	cout << s1 << endl;
	return 0;
}

控制台输出如下:
在这里插入图片描述

五.例题

1.字符串相加

点击链接

高精度,不能用整型或长整型直接相加。
运用string一位一位相加进位。

class Solution {
    
    
public:
    string s;
    string addStrings(string num1, string num2) {
    
    
        int end1 = num1.size()-1,end2 = num2.size()-1;
        int ret = 0,sum = 0;
        while(end1 >= 0 || end2 >= 0)
        {
    
    
            int n1 = end1>=0?num1[end1]-'0':0;
            int n2 = end2>=0?num2[end2]-'0':0;
            sum = n1+n2+ret;
            ret = sum/10;
            s += sum%10+'0';
            --end1;
            --end2;
        }
           if(ret == 1)
              s += '1';
        reverse(s.begin(),s.end());
        return s;
    }
};

2.字符串最后一个单词长度

1.牛客要包头文件 < string >
2.rfind()若没找到,返回无符号的-1
3.string::npos == 无符号的-1
4.注意只有一个单词的情况

#include <iostream>
#include<string>
using namespace std;
int main()
{
    
    
    string s1;
    getline(cin,s1);
    size_t eblack = s1.rfind(' '); //倒着找第一个空格,没找到返回无符号的-1
    if(eblack != string::npos)
    {
    
    
        cout << s1.size()-(1+eblack); //下标-1
    }
    else {
    
    
        cout << s1.size();
    }
    return 0;
}

3.反转字符串中的单词 III

点击链接

运用迭代器reverse函数

class Solution {
    
    
public:
    string reverseWords(string s) {
    
    
    int i = 0,k = 0;
    while(s[i]!='\0')
    {
    
    
        if(s[i] == ' ') //找空格
        {
    
    
            reverse(s.begin()+k,s.begin()+i); //借助reverse反转单词
            k = i + 1;
        }
        i++;
    }
    reverse(s.begin()+k,s.end()); //反转仅有或仅剩一个单词的情况
    return s;
    }
};

文末BB:对哪里有问题的朋友,尽管在评论区留言,若哪里写的有问题,也欢迎朋友们在评论区指出,博主看到后会第一时间确定修改。最后,制作不易,如果对朋友们有帮助的话,希望能给博主点点赞和关注.
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_73390155/article/details/132393949