前言:在日常的程序设计中,我们会经常使用到字符串。比如一个人的身份证号,家庭住址等,只能用字符串表示。在C语言中,我们经常使用字符数组来存储字符串,但是某些场景(比如插入,删除)下操作起来很繁琐。在C++中,底层设计了一个string类,将经常使用的功能封装在其中,使用起来非常简单,便捷。
本文主要介绍string的使用方法和几个常用函数,最后再列举几个经典例题。
我们使用string时经常会用到它封装的函数,这里推荐一个网站:C++ Reference。函数忘了怎么使用时可以参考该网站。
长话短说,不将它讲的太神秘:
string是用字符的顺序数组实现的类,用于管理字符数组。
我们平常怎么使用自定义的类,就怎么使用string。
格式:
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类型数据。
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;
}
这里就可以体现string的优点:
使用C语言,我们需要考虑给字符数组开多大的空间,而string不用我们考虑这点,我们只管输入,编译器会自动帮我们适配合适的空间。
int main()
{
string s1; //实例化一个对象s1
cin >> s1; // 输入
cout << s1 << endl; //打印
return 0;
}
string函数设计的接口,感兴趣的可以去文章开头给的网站看看。下面某些函数只给出常用的接口。
单从这个题目,就能让你感受到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;
}
返回string对象的有效字符长度(不包括\0),相当于C语言的strlen。
int main()
{
string s1("hello world");
cout << s1.size() << endl;
return 0;
}
返回string对象的容量
我们知道string会自动扩容,而它当然不是一个一个字节阔的,它是按照一定倍率扩容。
int main()
{
string s1("hello world");
cout << "size:" << s1.size() << endl;
cout << "capacity:" << s1.capacity() << endl;
return 0;
}
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;
}
string类对[]运算符进行了重载,使得我们可以借助[]像访问数组一样访问string。对于解决一些与字符串有关的题很有帮助。
以遍历string为例:
int main()
{
string s1("hello world");
for (size_t i = 0; i < s1.size(); i++) //运用size()函数
{
cout << s1[i] << " "; //下标访问
}
return 0;
}
在string对象尾部插入一个字符
int main()
{
string s1("hello world");
s1.push_back('x');
cout << s1 << endl;
return 0;
}
形式:append(字符串/string对象)
实际上设计的有些冗余,插入的话我们直接用+=就行了,何必再使用append函数呢?
int main()
{
string s1("hello world");
string s2("!!!");
s1.append("xxx");
s1.append(s2);
cout << s1 << endl;
return 0;
}
插入一个字符:
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;
}
函数名 | 作用(下面函数仅适用于正向迭代器!!!) |
---|---|
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;
}
问:可这样子不就是单纯的指针遍历吗,何必那么麻烦还使用迭代器?
答:迭代器的一个特点就是通用性,在vector,list,树等中都有迭代器。就以list举例,它是由一个一个的小结点组成的,物理空间中并不是连续的,使用结点指针++并不能找到它的下一个结点,但是像上述代码一样套上迭代器就可以遍历list。因此,迭代器遍历才是最主流的遍历方式。
函数名 | 作用(下面函数仅适用于反向迭代器!!!) |
---|---|
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种迭代器。
传入迭代器区间: reverse(s.begin(),s.end());
int main()
{
string s1("hello world");
reverse(s1.begin(), s1.end()); //传入迭代器区间
cout << s1 << endl;
return 0;
}
点击链接
高精度,不能用整型或长整型直接相加。
运用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;
}
};
1.牛客要包头文件 < string >
2.rfind()若没找到,返回无符号的-1
3.string::npos == 无符号的-1
4.注意只有一个单词的情况
#include
#include
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;
}
点击链接
运用迭代器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:对哪里有问题的朋友,尽管在评论区留言,若哪里写的有问题,也欢迎朋友们在评论区指出,博主看到后会第一时间确定修改。最后,制作不易,如果对朋友们有帮助的话,希望能给博主点点赞和关注.