文章目录
- 一、string介绍
- 二、string类对象的创建(常见构造)
- 三、string类对象的容量操作
- 1.size()和length()
- 2.capacity()
- 3.empty()
- 4.clear()
- 5.reserve()
- 6.resize()
- 四、string类对象的遍历与访问
- 1.operator[ ]
- 2.正向迭代器begin()和end()
- 3.反向迭代器rbegin()和rend()
- 4.const正向和反向迭代器
- 5.范围for
- 6.at()
- 五、string类对象的查找与修改
- 字符串查找
- 1.find()与npos
- 2.rfind()
- 3.find_first_of()和find_last_of()
- 4.substr()
- 字符串修改
- 1.operator+=
- 2.push_back()与pop_back()
- 3.append()
- 4.insert()
- 5.erase()
- 6.replace()
- 六、string类非成员函数
- operator+
- 输入getline
一、string介绍
string是表示字符串的字符串类。它提供了一系列成员函数和操作符,使得字符串的操作更加方便和灵活。它位于标准命名空间
std
下,因此通常需要使用using namespace std;
语句或者前缀std::
来引用。
string是C++的STL(standard template libaray-标准模板库)的容器之一。本篇,我们主要介绍string的基本用法。所有接口用法都可以在cplusplus参考手册中查阅。下面我们主要介绍string的一些常用接口。
string底层实际是basic_string类模板的实例化,是表示字符串类型的模板类。
二、string类对象的创建(常见构造)
string重载了很多构造和拷贝构造函数,其实常用的也就4种。
//常用构造方式
string s1;//无参构造
string s2("interesting");//构造
string s3(s2);//拷贝构造
string s4 = "hello world";//构造
不常用的初始化方式
//函数原型 string (size_t n, char c);
string s5(10, 'x');//10个字符x
//函数原型 string (const string& str, size_t pos, size_t len = npos);
string s6(s2, 8, 3);//打印ing 第8个位置开始拷贝3个字符
string s7(s2, 8);//打印ing 第8个位置开始拷贝后面所有字符
三、string类对象的容量操作
1.size()和length()
计算有效字符串长度,不包括
\0
。 size()与length()的底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
int main()
{
string s("abcde");
cout << s.size() << endl;//5
cout << s.length() << endl;//5
return 0;
}
2.capacity()
返回string类对象的空间容量大小。同样的一个字符串在不同编译器下的容量可能不一样。
string s("abcde");
cout << s.capacity() << endl;//vs2019:15
3.empty()
字符串为空返回1,非空则返回0。
一般也很少用到。我们也可以用str == ""
判断是否为空。
4.clear()
clear()只是将string中有效字符清空,不改变底层空间大小。 即有效数据size()清零,容量capacity()不变。
string s("abcde");
s.clear();
5.reserve()
提前申请空间,可以避免频繁扩容。有效数据size不变,容量capacity增大。 且只比指定容量要大,不会小。
在Windows环境下(如上图),reserve实际申请空间一般比我们指定的参数要大;而Linux的扩容空间与指定参数一样。
不同环境下,string扩容的规律也不同:Windows环境下(PJ版本STL),容量capacity每次以1.5倍的速度扩容;Linux环境下(SGI版本STL),每次以2倍速度扩容;所以频繁扩容的成本较高。reserve可以提前申请好空间,避免频繁扩容。
reserve可以缩容吗?
可以,但是如果原始空间有数据,则缩容无效;用clear清空有效数据,即可缩容到指定参数大小的空间。所以,想用reserve缩小空间
6.resize()
调整string容器大小,即有效数据个数size()。
string s("abcde");
s.resize(3);//abc size=3;
s.resize(5, 'x');//abcxx size=5
n < size相当于尾删,size()减小到n,底层空间capacity()不变。
n > size则size增加到n,多出的空间用第二个字符参数填充,无参则默认填充’\0’。
四、string类对象的遍历与访问
1.operator[ ]
int main()
{
string s("interesting");
for (size_t i = 0; i < s.size(); i++)
{
cout << s[i];
s[i]++;
}
return 0;
}
2.正向迭代器begin()和end()
begin()返回指向字符串第一个字符的迭代器,end()返回指向字符串末尾字符’\0’的迭代器。
迭代器提供了一种遍历容器中元素的方法,迭代器是像指针一样的类型,但可能是指针,也可能不是指针,用法与指针类似。
任何容器都支持迭代器,并且用法是类似的。 迭代器是属于类域的,所以要加域作用限定符。并且对于迭代器的使用,搭配关键字auto使用更简单。
int main()
{
string s("interesting");
//auto it = s.begin();//自动识别类型
string::iterator it = s.begin();
//打印interesting
while (it != s.end())
{
cout << *it;
it++;
}
return 0;
}
3.反向迭代器rbegin()和rend()
反向迭代器与正向迭代器用法一样,可以实现逆序遍历。注意正向和反向迭代器名字不一样。
int main()
{
string s("interesting");
//auto it = s.rbegin();//自动识别类型
string::reverse_iterator rit = s.rbegin();
//打印gnitseretni
while (rit != s.rend())
{
cout << *rit;
rit++;
}
return 0;
}
4.const正向和反向迭代器
对于const修饰的string类对象,就只能用const迭代器,否则会发生权限的放大。
使用auto更方便省事。
const string s("interesting");
//正向
string::const_iterator it = s.begin();
auto it = s.begin();
//反向
string::const_reverse_iterator rit = s.rbegin();
auto it = s.rbegin();
5.范围for
C++11支持更简洁的范围for的新遍历方式。范围for的底层就是迭代器,不支持迭代器就不支持范围for。
int main()
{
string s("interesting");
for (auto& ch : s)//可读 加上引用可写
{
ch++;
cout << ch;
}
return 0;
}
6.at()
使用与operator[ ]一样,但一般很少用at()来访问string。
at()与[ ]的区别是对越界的检查不一样:[ ]越界会断言,比较暴力;at()越界会抛异常,比较温和。
int main()
{
string s("interesting");
for (size_t i = 0; i < s.size(); i++)
{
cout << s.at(i);
s.at(i)++;
}
return 0;
}
五、string类对象的查找与修改
字符串查找
1.find()与npos
find是查找字符或字符串函数,返回与字符参数匹配到的第一个字符位置下标,如果找不到则返回npos。
npos是string类的静态成员变量,值为-1(-1的补码全是1),因为是size_t即无符号整型,所以npos表示的大小为232-1,即整型的最大范围。
2.rfind()
与find()一样,只不过rfind()是从右往左查找。
3.find_first_of()和find_last_of()
find_first_of():只要字符串中找到字符参数中任意一个字符,则返回第一个找到的下标。
例如,下面这段代码将字符串中的aeiou全部替换成*
find_last_of()同理,只不过是从右往左查找。
4.substr()
返回获取到的子串
int main()
{
string s1("hello world");
//第一种也是最常用的
string s2 = s1.substr(2, 3);//截取从下标2开始的3个字符
cout << s2 << endl;//llo
s2 = s1.substr(6);//截取从下标6开始后面的所有字符
cout << s2 << endl;//world
s2 = s1.substr();//截取整个字符串
cout << s2 << endl;//hello world
return 0;
}
字符串修改
1.operator+=
+= 可以尾插字符或者字符串,这个比较常用。+=操作不仅可以连接单个字符,还可以连接字符串。
int main()
{
string s("hello ");
s += "world";
s += '!';
cout << s << endl;
return 0;
}
2.push_back()与pop_back()
尾插和尾删单个字符
string s("abcd");
s.pop_back();//abc
s.push_back('x');//abcx
3.append()
尾插n个字符或者尾插指定区间的字符串
string s("hello ");
s.append(10, 'x');//尾插10个x
s.append("world", 3);//尾插字符串world的前3个字符
s.append("world", 1, 3);//尾插从下标1开始往后的3字符即orl
tip:一般push_back()和append()尾插不常用,用+=更方便省事。
4.insert()
在指定位置插入字符或者字符串
5.erase()
删除指定位置的字符或字符串
6.replace()
用新的字符或者字符串替换原字符串部分字符
六、string类非成员函数
operator+
尽量少用,因为传值返回,导致深拷贝效率低。
int main()
{
string s1("hello");
string s2("world");
s1 = s1 + " " + s2;
cout << s1 << endl;
return 0;
}
operator>>和operator<<运算符重载也是string的非成员函数。
输入getline
cin无法读取空格和换行,getline可以弥补这个缺陷。需要包含头文件
<string>
不指定分隔符,默认以回车结束,可以读取空格,常用来读取一行。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
getline(cin, s);
cout << s;
return 0;
}
第三个参数可以指定分隔符,例如指定’\r’可以读取回车,结束需按下:Ctrl+z 然后再回车
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
getline(cin, s, '\r');
cout << s;
return 0;
}