string类是由STL(标准模板库)中basic_string模板实例化而得到的模板类,其定义如下:
typedef basic_string<char> string;
【(知其源,格物致知)
题外篇:老祖宗模板类basic_string的原型:
template<class CharType, class Traits = char_traits<CharType>, class Allocator = allocator<CharType> >
class basic_string;
第一个参数Chartype:表示存储在basic_string字符串里的单个字符的数据类型。当CharType = char的时候,我们就定义了string
类,如本文第二行所示;
第二和第三个参数正如其名字,Trait(特点,特性),allocator(分配算符),一个描述了CharType元素的属性,另一个封装了有关
字符串内存分配和解除分配的详细信息。它们都设有了默认值(等号= 后边的即是)。
】
如何使用string类:
1.构造函数
对一串字符进行保存、处理,我们就先得存储它,string类就提供了这么一个存储的容器。下面是它的贴心服务:
string s1; //1.构造出名为s1的字符串,没给它装入任何元素(或许是为后续进一步的处理,或许仅仅为了好玩)此刻:s1 = ''
string s2("hello"); //2.我们一开始就知道的字符串并想要存储它(例如此例中的"hello"),我们就可以这样来声明。毋庸置疑:s2 = "hello"
string s3(4, 'K'); //3.两个参数,很奇怪吗?其实这样想就很自然:我们要存入100个字符'w',难不成全部写下来,这是件很考验人耐力的事情
//。像这样,第一个参数是一个整数,表明了第二个参数(字符)的个数。聪明的你会脱口而出:s3 = "KKKK"
string s4("12345", 1, 3); //4.你一定发现了双引号里面的内容就是一个字符串!!是的。后面两个数字,一个(此例为1)是下标,后一个是个数
//字是个数(此例为3)。这个函数就是把字符串"12345"的从下标为1开始之后的3个字符赋值为s4。显然:s4 = "234"
注意:在C++中(C中也是如此,值得注意的是,C语言中没有string类,它的字符串只能存储在char型数组内),数组、字符串等等的下标都是从0开始。虽然这在现实世界中显得有些不自然,但在计算机的很多方面,这样规定给了我们很大便利。举个例子:定义数组“int a[10];”,首地址为p,我们计算下标i的元素的地址就可以写成p+i*4(借此机会回忆一下数组地址的运算~)。如果下标从1开始,那我们就不得不写成p+(i-1)*4了,哪一种更简便呢。学过汇编代码对指针的运算会更有体会。
如果你真正理解了上述的构造函数,那你一定对函数里面什么地方用什么类型的参数了如指掌,所以你不会犯如下的错误:
错误实例1: string s_wrong1('w');//不适用字符型参数
错误实例2: string s_wrong2(123);//不适用整形参数
2.对string对象赋值
相比于构造函数,赋值可以使用更多的类型。char*型(就是字符串)的变量、常量可以为之赋值,这是肯定的。除此之外,char型的变量、常量也可以为之赋值。所以,我们可以猜得出来,string类中一定重载了“字符赋值给自身”的“=”(理解这句话涉及到重载的知识。简单的理解就是,“=”两边只能是相同类型的,重载了之后呢,就可以根据写的重载函数让两个不同类型的量可以接触咯,深入理解请好好查阅与“类”有关的知识)。
char c_string[5] = {"Jack"};
string s5;
s5 = c_string;//把char*型的变量赋值给s5
string s6;
s6 = "Queen";//把字符串赋值给s6(字符串说到底就是char*)
s6 = 'K'; //把字符赋值给s6
除了用等号赋值外,还可以利用string类的成员函数assign。具体操作示例如下(按照构造函数参数列表里的去理解):
string s1("beauty!"), s2, s3; //先声明三个字符串,s1 = "beauty!", s2 = "", s3 = ""(用到的是构造函数)
s3.assign(s1); //参数是一个字符串(变量,或者常量),将参数赋值给s3。s3 = s1
s2.assign(s1, 1, 2); //将s1的子串"ea"(下标一开始两个字符)赋值给s2。s2 = "ea"
s2.assign(4, 'z'); //将4个z赋值给s2。s2 = "zzzz"
s2.assign("ahhhha", 2, 4); //将字符串"ahhhha"的子串"hhha"赋值给s2。s2 = "hhha"
【题外:assign vt. "赋予,给予"之意。说实在的,以英语为母语的人学习程序语言很有优势,无论是函数名,还是类型名,都能够直观地看到它的意思,从而能更好的去理解它,谁叫这些语言是以英语为母语的一些大神发明出来的呢。打入汉字编程会是多么地亲切啊】
3.求字符串的长度
string有两个求长度的成员函数,length 和 size,返回值就是长度(int型)。
int len;
string s1("beauty!");
len = s1.length(); //方式1
len = s1.size(); //方式2
复习:s1返回的长度为7,但是却占据了8个字符的空间大小,这是因为字符串结尾都有休止符 '\0' 。休止符不计入长度。
对于char*类型的字符串,使用strlen()函数:
char c_string[5] = {"true"};
int len = strlen(c_string);
4.字符串的连接
自然的方法:使用 “+” 和“+=”,同样存在成员函数append,可以用来往字符串后边添加内容。
string s1("apple"), s2("pear"), s3;
s1.append(s2); // s1 = "applepear"
s1.assign(s2, 1, 2); // s1 = "appleea"
s1.assign(2, 'o'); // s1 = "appleoo"
s2.assign("aggood", 2, 4); // s2 = "peergood"
s3 = s1 + " and " + s2; // s3 = "apple and pear"
【append vt.附加,添上,贴上】
5.字符串的比较
首先要明确比较的规则:string对象比较大小时,是按词典顺序比较,而且大写比小写的小(大写的ASCII码比小写的靠前)。
同字符串的连接一样,有两类方式:
1.比较数字的字符用来比较字符串:"<" , ">" , ">=" , "<='", "=="
2.成员函数compare,根据返回值来判断:小于0说明自身小,等于0说明相等,大于0说明自身大。
string s1("universe"), s2("Am I larger than U?");
int n;
n = s1 < s2; //s1大于s2所以s1 < s2 为假,n = 0
//以下全部都是s1调用compare函数,参数列表中一定存在另一字符串B。B前面若存在参数,那一定有两个,说明比较的是s1的相应子串(规则与前文一致);若B后面
//那比较的也是B的相应子串
n = s1.compare(s2); // s1 与 s2 比较,因为s1 > s2,所以返回1,n = 1
n = s1.compare(1, 2, s2, 10, 6); //s1子串"ni"与s2子串"r than"比较,后者大,所以返回-1, n = -1
n = s1.compare(3, 2, s2); //s1子串"ve"与s2比较,前者大, n = 1
n = s1.compare("Universe"); //s1与"Universe"比较,小写字母大于大写字母, n = 1
n = s1.compare(1, 2, "Universe", 1, 2); //显然有, n = 0
6.string对象求子串
成员函数substr:
string substr(int n = 0, int m = string::npos) const;
可以用来求子串(n, m)。调用时,如果m默认或超过了字符串长度,则求出来的子串就是从下标n开始一直到字符串结束。
string s1 = "This is ready for substr()";
string s2 = s1.substr(2, 4); // s2 = "is i"
s2 = s1.substr(14); // s2 = "for substr()"
【subtract vt. 减去,扣除】
7.交换两个string对象的内容
成员函数:swap
string s1("河东"), s2("河西");
s1.swap(s2); // s1 = "河西", s2 = "河东"
8.string对象中的查找(内容较多)
查找对象:字符、整个字串、字串中的任一字符(有或者无);****第三个比较难理解,看了具体的函数就知道了*****
查找方向:从前往后、从后往前(字符串下标从0到末尾,0到末尾的方向即为从前往后);
返回值:所查找的字符或字串在原string对象中的位置(即下标),若是查不到,则返回string::npos。该值是string类中定义的一个静态常量。
1. (1) find:从前往后查找字串或字符出现的位置
(2) rfind:从后往前找字串或字符出现的位置
查找的参数很多样,举例如下:
string s("wisdom,brave in mind");
int n;
n = s.find('m'); //从前往后找第一个m,找到了返回其位置,找不到,返回string::npos。在这里,返回第一个m所在的下标,n = 5
if(n != string::npos)
cout << s.substr(n) <<endl; //输出: m,brave in mind
else
cout << "Not Found" << endl;
n = s.find("brave", 3);// 注意这里的意思:从s下标为3的地方寻找"brave",找到了,返回首字母的下标,即'b'的下标7
n = s.rfind("ev"); //s不存在"ev",但从后往前看好像有"ev",实则rfind只是从后往前找,找的字串还是从前往后读。故这里返回string::npos,输出后发现n = -1
n = s.rfind("ve"); //可以找到,返回v的下标,n = 10
2. (1) find_first_of :从前往后找,(注意下面这句很关键)何处出现了所查找字符串中的任一字符,返回第一个出现的字符下标
(2) find_last_of :(很有规律)从后往前找,balabalaa~
(此处往往有例子)
string s("wisdom,brave in mind");
int n;
n = s.find_first_of("heart");//从前往后查找s中第一次出现"heart"中任一字符,找呀找,找呀找,发现下标为8的r成为了“出头鸟”,于是n = 8
思维活跃、敢于挑战的人在此刻往往会尝试,find_first_of 的参数换做是一个字符会怎么样?我不会告诉你这是可以的~
3. (1) find_first_not_of:从前往后找,何处出现了所查找字符串没有包含的字符,返回第一个这样的字符(我相信下面一个函数的名字此刻一定在你口中呢喃)
(2) find_last_not_of :从后往前找,何处出现了所查找字符串没有包含的字符,返回第一个这样的字符
9.替换子串
顾名思义,可以对字符串进行这样的操作,用“另外的字符串”来替换自身的子串,二者长度可以不一样。
string s("God is a girl.");
s.replace(0, 3, "Moon light", 0, 4); //用"Moon light"的子串"Moon"来替换s的子串"God"。 s = "Moon is a girl"
s.replace(1, 2, 4, 'o'); //用4个'o'来代替"Moon"的子串"oo"。 s = "Moooon is a girl"
int n = s.find("Moooon"); //找到Moooon,返回首位字母的位置,n = 0
s.replace(n, 6, "God"); //已知被替换子串的长度6,用"God"替代之,s = "God is a girl"(写着写着想听这首歌了)
10.删除子串
erase【不禁想起了第一册英语书学过的eraser】
erase成员函数可以用来删除string对象中的子串
string s("It suddendly happened to me that");
s.erase(9, 1); //删除下标9起始的1个字符(去掉那个'd')。 s = "It suddenly happened to me that"
s.erase(2); //删除下标2以及之后的字符。 s = "It"
11.插入字符串
不像替换那样,怎样仅仅插入新的子串呢?又一个新的成员函数 insert
具体操作实例:
string s1("Wolf Jack wants more meat!"), s2("meat,");
s1.insert(15, "much "); //在下标15处插入"much"。 s1 = "Wolf Jack wants much more meat!"
s1.insert(25, s2); //在下标25处插入s2。 s1 = "Wolf Jack wants much more meat,meat!"
s1.insert(34, 4, '!'); //在下标34处插入4个'!'。 s1 = "Wolf Jack wants much more meat meat!!!!!"
12.string对象作为流处理
(先上理论部分)
使用流对象istringstream和ostringstream,可以将string对象当做一个流,进行输入和输出,使用这两个类需要包含头文件sstream。
(实例)
#include<iostream>
#include<sstream>
#include<string>
using namespace std;
int main(){
string src("I poccess 666.66 lucky 6");
istringstream istrStream(src); //建立src到istrStream的联系
string s1, s2;
int n;
double d;
char c;
istrStream >> c >> s1 >> d >> s2 >> n; //该过程和cin一样,只不过输入的来源由键盘变成了string对象src
ostringstream ostrStream;
ostrStream << c << " " << s1 << " " << d << " " << s2 << " " << n << endl; //输入到流ostrStream,结果保存在其管理的某处
cout << ostrStream.str(); //调用成员函数str(),将结果输入到屏幕上
return 0;
}
输出结果:
I poccess 666.66 lucky 6
--------------------------------
Process exited after 0.02357 seconds with return value 0
请按任意键继续. . .
13.用STL算法操作string对象
string对象可以看做是顺序容器,支持随机访问迭代器,有begin和end等成员函数。STL的许多算法也适用于string对象。(例子就不举了)
作者注:回想以往知识,感觉生疏大半,故开始利用起了这个平台适时地进行思考、总结。优快云平台,和其他同类IT论坛一样,有很多对这块领域感兴趣的人在此分享着自己的知识,一起交流学习希望这个平台能够督促我不断前行。文章有纰漏之处,还请各位网友指正!
ps:文章以我学过的一本教材为基础。