一、熟悉string类
1.1 string类的由来:
C语音中的字符串需要我们自己管理底层空间,容易内存泄露。而C++是面向对象语音,所以它把字符串封装成一个string类。
C++中对于string的定义为:typedef basic_string string; 也就是说C++中的string类是一个泛型类,由模板而实例化的一个标准类,本质上不是一个标准数据类型。
至于我们为什么不直接用String标准数据类型而用类是因为一个叫做编码的东西
我们每个国家的语言不同 比如说英语使用26个英文字母基本就能表述所有的单词 但是对于中文的字符呢?是不是就要用其他编码方式啊(比如说UTF-8或者GB2312)
1.2 string类的函数接口:
二、string类的写入:
2.1 string类的构造函数:
我们一般都是用无参的构造,拷贝构造和有参数的构造。
string s1;
string s2(s1);
string s3("hello C++");
string s4="hello C++";//s3 s4都是会把"hello C++"字符数组隐式转换成const char*类型,然后再调用这个构造函数,对s4进行构造
不需要担心空间不够问题,这些构造函数内部都会实现自动扩容的操作。
2.2 string类的数据读取:
方法一:string类里面对运算符[ ] 进行了重载,可以像使用数组一样使用。
for(int i=0;i<s1.size(),i++)
{
cout<<s1[i]<<" ";
}
这里的s1.size()是统计字符串长度(不包括\0)。如hello world的size是11
方法二:调用at函数
for(int i=0;i<s1.size(),i++)
{
cout<<s1.at(i)<<" ";
}
operator[ ]和at两种方法的区别:
下标访问报断言错误 at访问报异常
方法三:迭代器的方法。
迭代器的写法:首先写个string类名 后面跟上iterator(迭代器) 再后面加上一个it变量(可以是其他名字)
只时候就体现了auto的好处了 auto it = s1.begin(); //自动转换类型
string::iterator it=s1.begin();
while(it!=s1.end)
{
cout<<*it<<" ";
it++;
}
注意点:1. begin是指向开始位置,而end是指向\0位置---->左闭右开
2.不要写成it<s1.end,因为物理空间中不一定begin的就比end的小,如链表,树等是靠指针指向下一个节点。(而且底层不一定就是指针,这个后面讲)
3.为啥需要迭代器?统一操作,关心效果,不关心底层原理(C++面向对象思维)
这个代码的意思是,it指向字符串的第一个元素
(目前阶段可以理解为it就是一个指针)
(s1.begin()就是指向字符串第一个字符的指针)
在循环当中,如果it不等于最后一个字符所在的位置,就打印并且it++;
迭代器包括正向迭代器和反向迭代器,还有有无const修饰的迭代器
这里简单介绍一下 反向迭代器 和 const修饰的反向迭代器。
string::reverse_iterator it = s1.rbegin();
while(it!=s1.rend)
{
cout<<*it<<' ';
it++;
}
顾名思义,就是反向的,it++时是从后往前走的。
const修饰的反向迭代器
string::const_reverse_iterator it = s1.rbegin();
while (it != s1.rend())
{
cout << *it << " ";
it++;
}
方法四:范围for(底层原理是迭代器)
for(auto u:s1)
{
cout<<u<<" ";
}
auto 是实现自动识别类型, 如 char ch = ‘a’; auto i = ch; 则i的类型是char
这个代码的意思是 依次把s1中的字符串变量中的字符拷贝给u变量,然后打印u变量
如果想要对里面的值进行修改,就需要用引用传参。
for(auto& u:s1)
{
u++;
cout<<u<<" ";
}
其他:还有两个成员函数back和front,分别访问最后一个字符和第一个字符。
cout<<s1.back();
cout<<s1.front();
三、string类的容量接口:
3.1 size 和 length
s1="hello world";
cout<<s1.size()<<endl;
cout<<s1.length()<<endl;
两者没有区别,一开始是length先出来的,然后为了和后面的STL中的其他容器保持一致,就多了一个size函数。我们一般用size
3.2 capacity
capacity返回的容量不包括\0,就是本来开了16的空间,但是前人认为\0不能算有效容量,就会返回15
3.3 empty
string s1;
string s2="hello world";
cout<<s1<<endl;
cout<<s2<<endl;
3.4 clear
将size变为0,相当于清理数据,下一次是从size==0的位置开始插入数据。
string s2="hello world";
s2.clear();
s2 = "C++";
cout << s2;
3.5 reserve 预留空间 和 resize 调整size的大小,其间用\0来填充
string s1="hello world";
string s2="hello world";
s2.reserve(100);
cout<<s2.size()<<" "<<s2.capacity() << endl;
s1.resize(100);
cout << s1.size() <<" "<< s1.capacity() << endl;
这样子的结果是如果是resize出来的,那么前面的11--100的空间都被\0填充,我们只能从100号位置开始填充,中间的空间都浪费掉了。
而reserve出来的没有改变size的大小,就扩容。(这个函数就很nice,当你提前知道你想要开多大的空间,就可以使用这个函数。
看resize函数的代码
string s1="hello world";
s1.resize(15);
s1 += "hi";
cout << endl;
cout << s1 << endl;
cout << s1.size() <<" "<< s1.capacity() << endl;
\0是不打印出来的,所以最后的结果是
world和hi中间是没有空格的
四、String类对象修改接口
4.1 这里就operator+=这个函数重要
可以实现尾插字符,也可以实现尾插字符串
第四个是C++11之后添加的(我还不知道是什么,等我学了再谈) 现在我们就用前三种。
string s1 = "hello world";
s1 += ' ';
s1 += "hello C++";
cout << s1;
4.2 insert和earse
都是需要挪动数据,所以时间复杂度高,少用。
string s1 = "hello world";
s1.insert(5," ");
cout << s1;
从下标为5位置开始插入
string s1 = "hello world";
s1.insert(5," ");
s1.erase(0,5);
cout << s1;
从下标为0的位置,开始删除5个元素
string类里面的erase函数是有缺省值的。
当不传参的时候是从0号位置开始删除到末尾。
传一个参数是指从n号位置开始一直删除到末尾。
4.3 replace
函数的参数分别是替换的起始位置,删除的数据大小,替换的字符串(不能是字符)
把含有a b c的字符都替换成 字符 *
其他 reverse(这个不是string类里面的,而是迭代器中的)
逆置字符串,参数是迭代器s1.begin()和s1.end()
s1="hello world";
reverse(s1.begin(),s1.end());
cout<<s1;
就可以把s1中的字符串逆转,当然,也适用于其他的迭代器。
五、 String对象字符串运算相关接口
5.1 c_str
两者的类型不同。
一个是string对象
一个是字符指针(也就是字符串)
5.2 find
第一种:
string s1 = "hello world";
string s2 = "world";
size_t posn=s1.find(s2, 1);
cout << posn;// 从s1这个对象中查找s2这个对象,从下标为1这个位置查找(默认从0开始查找)
返回 匹配的第一个字符的下标
第二种:
string s1 = "hello world";
string s2 = "world";
size_t posn = s1.find("hello"); //第一个参数是输入字符串 第二个参数默认为0
cout << posn;
第三种:
参数1、查找的字符串 参数2、从第几个位置开始查找 参数3、要匹配的字符序列的长度。
string str("There are two needles in this haystack with needles."); found = str.find("needles are small", 15, 6); if (found != std::string::npos) cout << "second 'needle' found at: " << found << '\n';
从下标为15的位置开始查找,找到needle这个字符串(查找这个字符串的前6个字符)
第四种:
查找字符
string str("There are two needles in this haystack with needles."); found = str.find('.'); if (found != std::string::npos) cout << "Period found at: " << found << '\n';
5.3 rfind
就是倒着找,从后往前找
同样是四个重载
5.4 substr
从第pos位置开始截取len长度的字符串。参数2没有时,取到结尾。
#include <iostream> // std::cout
#include <string> // std::string
int main()
{
std::string str = "We think in generalities, but we live in details.";
// (quoting Alfred N. Whitehead)
std::string str2 = str.substr(3, 5); // "think"
std::size_t pos = str.find("live"); // position of "live" in str
std::string str3 = str.substr(pos); // get from "live" to the end
std::cout << str2 << std::endl << str3 << '\n';
return 0;
}
5.5 getline(string)
两种重载,第一种需要自己手动设置结束符 第二种是以\n为结束符
//第一种
int main()
{
std::string name;
std::cout << "Please, enter your full name: ";
std::getline(std::cin, name,'a');
std::cout << name;
return 0;
}
以‘a’字符为结束标志
//第二种
#include <iostream>
#include <string>
int main()
{
std::string name;
std::cout << "Please, enter your full name: ";
std::getline(std::cin, name);
std::cout << name;
return 0;
}
好了,到这里本篇文章就结束了,如果有什么错误的地方,还请各位大佬指明,相互学习。