参考文献:C++标准库官网
前言:在C/C++的学习过程当中一定一定要多刷题,牛客网作为国内内容超级丰富的IT题库,尤其是它的C、C++,有从入门到大厂真题,而且大部分的考试题目也是从中抽取,还有很多面经,推荐大家去牛客网进行刷题练习,点击链接:牛客网刷题入口
文章目录
- 前言
- 一.string类对象常见的构造函数
- 二.string类对象的访问及遍历操作
- 1.operator下标[ ]
- 2.范围for(C++11支持)
- 3.迭代器
- 三.string类对象的容量操作
- 1.基本:size,capacity,clear,empty
- 2.reserve函数
- 3.resize函数
- 四.string类对象的修改操作
- 1.operator+=
- 2.append
- 3.push_back尾插
- 4.insert与erase插入与删除
- 5.assign与replace
- 6.compare字符串比较
- 7.swap交换
- 8.小问题:利用replace将空格都替换成%20涉及find和replace
- 9.如何获取一个文件名的后缀
前言
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模板类
一.string类对象常见的构造函数
我们现在来使用一下这些常见的字符串构造函数:
#include<iostream>
#include<string>
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;
}
二.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];
}
现在再来看看如何用下标遍历string类对象,s1可以直接调用关于string类对象容量操作的函数s1.size(),可以得到s1的长度。注意:下列代码的长度为4,size计算长度时不包括’\0’,类似strlen函数。
#include<iostream>
#include<string>
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;
}
2.范围for(C++11支持)
若需要通过范围for修改对象的元素,则用于接收元素的变量e的类型必须是引用类型,否则e只是对象元素的拷贝(类似形参),对e的修改不会影响对象的元素。
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s1("1234");
for (char& e : s1)//如果需要修改,必须加&引用,这里也可用auto& e
{
e++;
}
cout << s1 << endl;//2345
return 0;
}
3.迭代器
先来看看C++标准库中有关迭代器的函数接口,这里我们重点学习begin,end,rbegin,rend。
正向迭代和反向迭代:正向迭代器使用end和begin函数接口,反向迭代器使用rend和rbegin函数接口。
#include<iostream>
#include<string>
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
const正向,const反向迭代器:只支持读数据,不能修改容器数据
//const正反向:只支持读,不能修改容器数据
#include<iostream>
#include<string>
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;
}
三.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,即相当于插入数据
n>15相当于扩容+插入数据
#include<iostream>
#include<string>
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;
}
四.string类对象的修改操作
1.operator+=
C++标准库的运算符重载函数声明:
//string类对象的修改
#include<iostream>
#include<string>
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;
}
2.append
功能与+=相似
#include <iostream>
#include <string>
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;
}
3.push_back尾插
#include <iostream>
#include <string>
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;
}
4.insert与erase插入与删除
#include <iostream>
#include <string>
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;
}
#include <iostream>
#include <string>
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;
}
5.assign与replace
#include <iostream>
#include <string>
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;
}
6.compare字符串比较
注意比较的是ASCII码值。
1.两个字符串相等,则返回0;
2.比较字符串中第一个不匹配的字符值较小,或者所有比较字符都匹配,但比较字符串较短,则返回小于0的值。
3.比较字符串中第一个不匹配的字符值较大,或者所有比较字符都匹配,但比较字符串较长,则返回大于0的值。
#include <iostream>
#include <string>
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;
}
7.swap交换
#include <iostream>
#include <string>
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;
}
8.小问题:利用replace将空格都替换成%20涉及find和replace
#include <iostream>
#include <string>
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;
}
9.如何获取一个文件名的后缀
#include <iostream>
#include <string>
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;
}