STL学习(2)-string

博文内容是学习很多博客所总结的,类似学习笔记,如有错误欢迎指出!


什么是string


类似Java中的String类,使用容器实现了字符串的操作,但是本质仍然是数组,所以也能使用数组的操作方式。我们在C语言中使用char类型来模拟字符串。而STL已经创建好了string对象,我们可以像Java使用String类一样方便地使用string对象。首先,我们要先引入头文件:

#include<string>

引入之后我们就可以方便地使用string了。具体的使用方法请见下文。

初识string-定义、初始化与输入


string的声明方法有非常多种,用哪一种都非常方便,下面几种方法都是定义了一个字符串"hello string!"

string s1 = "hello string!";
string s2("hello string!");
string s3;
s3 = "hello string!";

当然,习惯用哪一种就可以用哪一种。最常用的还是从键盘读入,下面也有几种常用的读取方法:

string s1;
cin >> s1;
getline(cin, s1);
getline(cin, s1, 'a');

getline()这个函数用处是比较大了。如果使用cin的时候,碰见空格、回车符,就算是字符串的结束,例如:

string s;
cin >> s;
cout << s << endl;

//input:aaa bbb
//output:aaa

空格后面就不再读取了(如果是cin >> s1 >> s2;那么s2将获得值"bbb"),如果我们想把空格也读取,就是说直接读取一行,就要使用getline()函数。即:

string s;
getline(cin,s);
cout << s << endl;

//input:aaa bbb
//output:aaa bbb

这个函数有第三个参数,可以一直从标准输入设备中读取字符,直到碰见该字符再停止,回车符也能够读取。例:

string s;
getline(cin,s,'s');
cout << s << endl;

//input:aaa bbb ccs
//output:aaa bbb cc

可以看到碰见's'就停止了,当然这个's'也没有读入。
不过既然是整行读取,使用getline()就能碰到C/C++经典问题——吃回车。这个问题大一的时候还是挺困扰的,据说谭X也经常拿这个出来考试。不过大家都是学计算机的,这个问题可以一定要好好解决的,博主就不再赘述了,如果没碰见这个问题的同学,连续写2个getline()就能发现问题了(笑),解决方法敬请百度。

当然,C语言之前怎么用字符串,C++还是能使用的,但是gets函数这个还是推荐不使用吧。另外string如果用scanf的话有这样的操作:

string s1;
s1.resize(10);
scanf("%s", &s1[0]);

等于说我们在声明一个string后,直接对其分配了空间,然后用scanf就能读取数据了。但是注意,我们开辟的空间(就是使用resize函数)有多大,读取的时候最多就是多大,比如我们定的是10,那么就最多读取10个字符。这里注意:这里的10就是有效数据10个,不是跟C语言一样,考虑最后预留一位给 '\0' 。
但是,加入我们没有读取够10个字符,后面的就会被空格补充。见下例:

string s1;
s1.resize(10);
scanf("%s", &s1[0]);
cout << s1;
input: 0123
output: 0123      请按任意键继续. . .

output是博主的控制台输出结果。由于没有输出换行,所以可以看出有空格的存在。

常用方法


首先列举一下常用的string方法,这些并不是全部方法,博主只写了一些经常使用的:

begin()     //得到指向字符串开头的Iterator
end()       //得到指向字符串结尾的Iterator
rbegin()    //得到指向反向字符串开头的Iterator
rend()      //得到指向反向字符串结尾的Iterator
size()      //得到字符串的大小
length()    //和size函数功能相同
empty()     //判断是否为空
push_back() //将一个字符压栈
erase()     //删除字符串
clear()     //清空字符容器中所有内容

这些方法在面会用一些例子来讲解一下该如何使用,上面这些是简单的总结。

这里单独说一下compare()函数

string s = "str";
cout << s.compare("strr") << endl;
output:-1

这个compare表示字符串与另一个字符串进行比较,例如代码中的"str"与"strr",根据ASCII码,如果原字符串相比比较小,那么返回值是“-1”,如果一样返回值是“0”,如果比较大返回值是“1”。
我们可以利用这个函数进行字符串之间的比较。

string基本操作-遍历与读取


我们可以用cout来输出整个字符串,有的时候也需要对字符串中的字符进行操作。读取与遍历是最常见的操作了,下面有一些例子,请看:

#include<iostream>
#include<string>

using namespace std;

int main() {
	string s = "0123456789";
	for (int i = 0; i < s.size(); i++) {
		cout << s[i] << " ";
	}
	cout << endl;
	cout << "char 2 is " << s[1] << endl;
	string::iterator it;
	for (it = s.begin(); it != s.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
	string::reverse_iterator r_it;
	for (r_it = s.rbegin(); r_it != s.rend(); r_it++) {
		cout << *r_it << " ";
	}
	cout << endl;

	system("pause");
	return 0;
}

上面的代码是不是看到string::iterator瞬间懵逼了?不要担心,由于这里跟迭代器有关,博主分开来写了,如果看不懂可以百度一下C++STL的迭代器用法以及逆向迭代用法,或者看博主的另一篇博文:
 留坑- -
这里只用了解使用for循环的遍历方法就行了。
我们可以看出来,string也可以像数组一样直接得到某一个位置的数据。但是注意,此时获取的数据是char类型的,所以要:

char c = s[1];

上面的代码输出结果就是下面的:


0 1 2 3 4 5 6 7 8 9
char 2 is 1
0 1 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 1 0

另外有种C++的foreach遍历方法,感兴趣的可以百度,博主不再赘述了,挺好用的。

string基本操作-删除与清空


一般来说不太常需要删除与清空这些操作,至少我是用的比较少,数组的话在竞赛中经常使用memset来初始化(要了解memset的初始化原理,一般来说初始化0或者无限大0x3f3f3f3f,如果是其他数还是不能用memset的),string就用的很少了,这里也会介绍一下顺便用一下上面没有提到的方法:

#include<iostream>
#include<string>

using namespace std;

int main() {
	string s = "rua!";
	if (!s.empty()) {
		cout << "string is " << s << endl;
		s.clear();
	}
	if (s.empty()) {
		cout << "clear!" << endl;
	}

	system("pause");
	return 0;
}

bool empty(),如果字符串为空返回true,否则为false,博主在自己写一些判定为空的函数一般是定义为:

bool isEmpty()

可能是Java留下的习惯吧,不过逻辑上可以一眼看出来为空的时候返回true。

clear(),顾名思义就是清空string,变成空字符串。

下面的删除就是稍微多一点了,使用的函数是erase()。针对string的区间进行删除操作,有多个重载函数方便完成不同的操作。简单说,总共有3种erase()的用法,下面的代码会解释一下这3种用法:

#include<iostream>
#include<string>

using namespace std;

int main(){
	string s1 = "hello world!";
	s1.erase(2, 3); // 删除从下标2开始的3个字符
	cout << s1 << endl;

	string s2 = "hello world!";
	string::iterator it = s2.begin() + 2;
	s2.erase(it); // 删除当前迭代器指向位置的字符
	cout << s2 << endl;

	string s3 = "hello world!";
	
s3.erase(s3.begin()+1, s3.end());

// 删除2个指针之间的字符(不包括第二个参数)cout << s3 << endl;system("pause");return 0;}

输出如下:

he world!
helo world!
hlo world!
请按任意键继续. . .

每个用法已经在注释中标记。可以比较一下实际输出。第二种用法要用到迭代器,实际上删除的是迭代器指向的字符。第三种是直接删除2个指针之间的数据,这里注意,第二个参数是不被包括的。例如上面代码,删除的是"hello"中的"el",即从begin()+1开始删除,到begin()+3的前一位为止。

关于第三种用法为什么要这样写,这里给出博主的个人见解,首先看一下这句代码:

s3.erase(s3.begin()+1, s3.end());
我们知道,end()函数指向的是最后一位的下一个内存地址,如果erase()函数包括第二个参数的话,等于说要删除一个不存在的内存空间。为了保护,不包括第二个参数是很稳妥的,这样最多只能删除到最后一个字符。


string基本操作-字符串操作


这里的一些函数就与C语言中string.h库中的函数有关了,至少名字是一样的,但是用法换成了面向对象的思想,就是封装进了string中。首先我们谈一下字符串的拼接,例:

string s1 = "aa";
string s2 = "bb";
string s3 = s1 + s2;
cout << s3 <<endl;
output:aabb

这个就非常简单了,使用+号就能直接将2个字符串进行拼接,但是注意没有“减号”这个说法的。

string.h库中有经典函数strcat()与strcpy(),可以拼接字符串,由于C++可以使用“加号”直接拼接,比subcat方便许多,所以基本不些函数。还有一个经典函数substr(),这个函数在string中用的非常多,可以截取一段字符并返回string类型。例:

string s = "0123456789";
string ans = s.substr(0, 5);
cout << ans << endl;
output:01234

可以看到,substr的2个参数中,第一个参数表示截取的起点下标,第二个参数表示截取的长度(包括第一个下标)。代码就是从s[0]开始,截取5位,那么就是01234。

string操作-插入


string属于顺序存储结构,底层使用了数组来实现,学过数据结构就能知道,数组的插入操作是非常浪费时间的。算法复杂度上是O(n),而链表的插入操作仅仅为O(1)。所以string虽然也有插入操作,但是效率非常低。不过string的插入可以插入另一个字符串,不追求速度的情况下还是比较方便的。

string的插入也是有insert函数,其他的容器类很多也有,这里提一下一些用法即可:

string s = "str";
string ans = "233";
s.insert(1, ans);
cout << s << endl;
output:s233tr

最基本的用法,insert的2个参数中,第一个参数表示从下标1开始插入,第二个参数表示插入字符串。可以看到,是从下标1开始插入的,所以s[1] = ans[0],并不是下标1的元素不变,这里需要注意一下。

string s = "str";
char ans = '6';
string::iterator it = s.begin();
for (; *it != 't' && it != s.end(); it++);
if (it != s.end()) {
	s.insert(it, ans);
}
output:s6tr

这里又牵扯到迭代器了,不懂迭代器的可以放一放。这里是先用迭代器迭代到适应位置。博主利用了for循环,但是没有循环体,而是满足条件迭代到字符串s的‘t’字符或者迭代完毕退出循环。下面的if先判断一下迭代器的位置,是不是迭代了整个字符串发现并没有字符't'。然后,insert的第一个参数是iterator类型,第二个参数必须是char类型,不能为string。表示在当前iterator所在位置插入字符。

上面这个操作也可以不使用迭代器来实现:

string s = "str";
string ans = "233";
for (int i = 0; i < s.size(); i++) {
	if (s[i] == 't') {
		s.insert(i, ans);
		break;
	}
}
cout << s << endl;

可见,这样还能插入字符串。只是注意,不要忘记break,否则就会陷入死循环了。

string操作-查找


跟其他容器一样,利用了find函数,例:

string s = "str233";
string f = "r2";
cout << s.find(f) << endl
output:2

就是查找第一个出现匹配目标的下标。这里注意,这个find函数并不是algorithm库中的find函数,不要搞混了。这个是string类所实现的一个方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值