- 除了顺序容器共同的操作之外,string类型还提供了一些额外的操作。这些操作中 的大部分要么是提供string类和C 风格字符数组之间的相互转换,要么是增加了允许我们用下标代替迭代器的版本。
- 标准库string类型定义了大量函数。幸运的是,这些函数使用了重复的模式。由于 函数过多,本节初次阅读可能令人心烦,因此读者可能希望快速浏览本节。当你了解
string支持哪些类型的操作后,就可以在需要使用一个特定操作时回过头来仔细阅读
9 .5 .1 构造string的其他方法
- 除了我们在3.2.1节 (第76页)已经介绍过的构造函数,以及与其他顺序容器相同的构造函数(参见表9.3,第299页)外,string类型还支持另外三个构造函数,如表9.11 所示。
- 这些构造函数接受一个string或一个const char*参数,还接受(可选的)指定拷贝多少个字符的参数。当我们传递给它们的是一个string时,还可以给定一个下标来指出从哪里开始拷贝:
const char *cp = "Hello World!!!"; //以空字符串结束的数组
char noNull[] = {'H','i'}; // 不是以空的字符串结束
string s1(cp); //拷贝cp中的字符直到遇到空的字符串 ,因此 s1 == "Hello World!!!"
string s2(noNull,2); //拷贝两个字符串 s2 == "Hi"
string s3(noNull);//未定义 noNull不以空字符结束
string s4(cp + 6,5);//从cp[6]开始拷贝5个字符 s4 == "Word"
string s5(sl,6,5);//从s1[6]开始拷贝5个字符 s5 == "Word"
string s6(s1,6);//从s1[6]开始拷贝直至末尾 s6 == "Word"
string s7(s1,6,20);//从s1[6]开始拷贝直至末尾 s7 == "Word"
string s8(s1,16);//抛出一个out_of_range的异常
- 通常当我们从一个const char*创建string时,指针指向的数组必须以空字符结 尾,拷贝操作遇到空字符时停止。如果我们还传递给构造函数一个计数值,数组就不必以空字符结尾。如果我们未传递计数值且数组也未以空字符结尾,或者给定计数值大于数组大小,则构造函数的行为是未定义的。
- 当从一个string拷贝字符时,我们可以提供一个可选的开始位置和一个计数值。开始位置必须小于或等于给定的string的大小。如果位置大于size,则构造函数抛出一 个 out_of_range异常 (参见5.6节,第 173页)。如果我们传递了一个计数值,则从给定位置开始拷贝这么多个字符。不管我们要求拷贝多少个字符,标准库最多拷贝到string 结尾,不会更多。
substr操作
- substr操作 (参见表9.12) 返回一个string,它是原始string的一部分或全部的拷贝。可以传递给substr 一个可选的开始位置和计数值
- 如果开始位置超过了 string的大小,则 substr函数抛出一个out_of_range异常(参 见 5.6节,第 173页)。如果开始位置加上计数值大于string的大小,则 substr会调 整计数值,只拷贝到string的末尾。
9.5.2改变string的其他方法
- string类型支持顺序容器的赋值运算符以及assign、insert和erase操作(参见9.2.5节,第302页;9.3.1节,第306页;9.3.3节,第311页)。除此之外,它还定义了额外的insert和erase版本。除了接受迭代器的insert和erase版本外,string还提供了接受下标的版本。下标指出了开始删除的位置,或是insert到给定值之前的位置:
- s.insert(s.size(),5,'!');//在s末尾插入5个感叹号
- s.erase(s.size()-5,5);//从s删除最后5个字符
- 标准库string类型还提供了接受C风格字符数组的insert和assign版本。例如,我们可以将以空字符结尾的字符数组insert到或assign给一个string:
- const char*cp="Stately,plumpBuck";
- s.assign(cp,7);//s=="Statelyn"
- s.insert(s.size(),cp+7);//s=="Stately,plumpBuck”
- 此处我们首先通过调用assign替换s的内容。我们赋予s的是从cp指向的地址开始的7个字符。要求赋值的字符数必须小于或等于cp指向的数组中的字符数(不包括结尾的空字符)。
- 接下来在s上调用insert,我们的意图是将字符插入到s[size()]处(不存在的)元素之前的位置。在此例中,我们将cp开始的7个字符(至多到结尾空字符之前)拷贝到s中。
- 我们也可以指定将来自其他string或子字符串的字符插入到当前string中或赋予当前string:
- strings="some string",s2=nsome other string";s.insert(0,s2);//在s中位置0之前插入s2的拷贝
- //在s[0]之前插入s2中s2[0]开始的s2.size()个字符
- s.insert(0,s2,0,s2.size());
append和replace函数
- string类定义了两个额外的成员函数:append和replace,这两个函数可以改变string的内容。表9.13描述了这两个函数的功能。append操作是在string末尾进行插入操作的一种简写形式:
- strings("C++Primer"),s2=s;//将s和s2初始化为"C++Primer*'
- s.insert(s.size(),"4thEd");//s=="C++Primer 4th Ed.
- s2.append("4thEd");//等价方法:将"4thEd."追加到s2; s==s2
- replace操作是调用erase和insert的一种简写形式:
- //将“4th”替换为"5th"的等价方法
- s.erase(11,3);//s=="C++Primer Ed."
- s.insert(11,"5th");//s=="C++ Primer 5th Ed.”
- //从位置11开始,删除3个字符并插入5th
- s2.replace(11,3,"5th");//等价方法:s==s2
- 此例中调用replace时,插入的文本恰好与删除的文本一样长。这不是必须的,可以插入一个更长或更短的string:
- s.replace(11,3,'Fifth');//s=="C++Primer Fifth Ed 在此调用中,删除了3个字符,但在其位置插入了5个新字符
改变string的多种重载函数
- 表9.13列出的append、assign,insert和replace函数有多个重载版本。根据我们如何指定要添加的字符和string中被替换的部分,这些函数的参数有不同版本。幸运的是,这些函数有共同的接口。
- assign和append函数无须指定要替换string中哪个部分:assign总是替换string中的所有内容,append总是将新字符追加到string末尾。replace函数提供了两种指定删除元素范围的方式。可以通过一个位置和一个长度来指定范围,也可以通过一个迭代器范围来指定。insert函数允许我们用两种方式指定插入点:用一个下标或一个迭代器。在两种情况下,新元素都会插入到给定下标(或迭代器)之前的位置。
- 可以用好几种方式来指定要添加到string中的字符。新字符可以来自于另一个string,来自于一个字符指针(指向的字符数组),来自于一个花括号包围的字符列表,或者是一个字符和一个计数值。当字符来自于一个string或一个字符指针时,我们可以传递一个额外的参数来控制是拷贝部分还是全部字符。
- 并不是每个函数都支持所有形式的参数。例如,insert就不支持下标和初始化列表参数。类似的,如果我们希望用迭代器指定插入点,就不能用字符指针指定新字符的来源。
9.5.3string搜索操作
- string类提供了6个不同的搜索函数,每个函数都有4个重载版本。表9.14描述了这些搜索成员函数及其参数。每个搜索操作都返回一个string::size_type值,表示匹配发生位置的下标。如果搜索失败,则返回一个名为string::npos的static成员(参见7.6节,第268页)。标准库将npos定义为一个conststring::size_type类型,并初始化为值-1。由于npos是一个unsigned类型,此初始值意味着npos等于任何string最大的可能大小(参见2.1.2节,第32页)。
- find函数完成最简单的搜索。它查找参数指定的字符串,若找到,则返回第一个匹配位置的下标,否则返回npos:
- string name("AnnaBelle");
- auto posl=name.find("Annan");//posl==0
- 这段程序返回0,即子字符串"Anna"在"AnnaBelle”中第一次出现的下标。搜索(以及其他string操作)是大小写敏感的。当在string中查找子字符串时,要注意大小写:
- string lowercase("annabelle**);
- posl=lowercase.find("Anna");//posl==npos
- 这段代码会将posl置为npos,因为Anna与anna不匹配。
- 一个更复杂一些的问题是查找与给定字符串中任何一个字符匹配的位置。例如,下面代码定位name中的第一个数字:
- string numbers("0123456789n"),name("r2d2");
- autopos=name.find_first_of(numbers);//返回1,即,name中第一个数字的下标
- 如果是要搜索第一个不在参数中的字符,我们应该调用find_first_not_of。例如,为了搜索一个string中第一个非数字字符,可以这样做:
- string dept("03714p3");
- autopos=dept.find_first_not_of(numbers); //返回5--字符,p,的下标
指定在哪里开始搜索
- 我们可以传递给find操作一个可选的开始位置。这个可选的参数指出从哪个位置开 始进行搜索。默认情况下,此位置被置为0。一种常见的程序设计模式是用这个可选参数在字符串中循环地搜索子字符串出现的所有位置:
string::size_type pos = 0;
//每步循环查找name中的一个数
while((pos = name.find_first_of(numbers,pos))//numbers表示我们要找到的是数字!= string::npos)//npos是指string的末尾最后的位置,一般是std::container::size_typestd::cout << "found number at index" << pos << "element is "<< name[pos] << endl;++pos;//移动到下一个字符
}
- while的循环条件将pos重置为从pos开始遇到的第一个数字的下标。只要 find_first_of返回一个合法下标,我们就打印当前结果并递增pos。 如果我们忽略了递增pos,循环就永远也不会终止。为了搞清楚原因,考虑如果不做递增运算会发生什么。在第二步循环中,我们从pos指向的字符开始搜索。这个字符是—个数字,因此find_first_of会 (重 复 地 )返 回 pos!
逆向搜索
- 到现在为止,我们已经用过的find操作都是由左至右搜索。标准库还提供了类似的, 但由右至左搜索的操作。rfind成员函数搜索最后一个匹配,即子字符串最靠右的出现位置:
- string river("Mississippi");
- auto first_pos = river. find ("is") ; // 返回 1
- auto last_pos = river. rfind (nisn) ; // 返回 4
- find返回下标1,表示第一个"is”的位置,而 rfind返回下标4,表示最后一个"is”的位置。
- 类似的,find_last函数的功能与find_first函数相似,只是它们返回最后一个而不是第一个匹配:
- find_last_of搜索与给定string中任何一个字符匹配的最后一个字符。
- find_last_not_of搜索最后一个不出现在给定string中的字符。
- 每个操作都接受一个可选的第二参数,可用来指出从什么位置开始搜索。
9.5.4compare函数
- 除了关系运算符外(参见3.2.2节,第79页),标准库string类型还提供了一组compare函数,这些函数与C标准库的strcmp函数(参见3.5.4节,第109页)很相似。类似strcmp,根据s是等于、大于还是小于参数指定的字符串,s.compare返回0、正数或负数。
- 如表9.15所示,compare有6个版本。根据我们是要比较两个string还是一个string与一个字符数组,参数各有不同。在这两种情况下,都可以比较整个或一部分字符串
9.5.5数值转换
- 字符串中常常包含表示数值的字符。例如,我们用两个字符的string表示数值15一字符1,后跟字符5。--般情况,一个数的字符表示不同于其数值。数值15如果保存为16位的short类型,则其二进制位模式为0000000000001111,而字符串"15"存为两个Latin-1编码的char,二进制位模式为0011000100110101。第一个字节表示字符1,其八进制值为061,第二个字节表示字符5,,其Latin-1编码为八进制值065。
- 新标准引入了多个函数,可以实现数值数据与标准库string之间的转换:
- int i=42;string s=to_string(i);//将整数i转换为字符表示形式
- doubled=stod(s);//将字符串s转换为浮点数
- 此例中我们调用to_string将42转换为其对应的string表示,然后调用stod将此string转换为浮点值。
- 要转换为数值的string中第一个非空白符必须是数值中可能出现的字符:
- string s2=“pi=3.14”;
- //转换s中以数字开始的第一个子串,结果d=3.14
- d=stod(s2.substr(s2.find_first_of(*'+-.0123456789*')));
在这个stod调用中,我们调用了find_first_of(参见9.5.3节,第325页)来获得s中第一个可能是数值的一部分的字符的位置。我们将S中从此位置开始的子串传递给
stodostod函数读取此参数,处理其中的字符,直至遇到不可能是数值的一部分的字符。然后它就将找到的这个数值的字符串表示形式转换为对应的双精度浮点值。 - string参数中第一个非空白符必须是符号(+或-)或数字。它可以以Ox或0X开头来表示十六进制数。对那些将字符串转换为浮点值的函数,string参数也可以以小数点(.)开头,并可以包含e或E来表示指数部分。对于那些将字符串转换为整型值的函数,根据基数不同,string参数可以包含字母字符,对应大于数字9的数。