C++ Primer 额外的string操作
专栏简介:本专栏主要面向C++初学者,解释C++的一些基本概念和基础语言特性,涉及C++标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级程序设计技术。希望对读者有帮助!
目录
- 9.5额外的string操作
- 构造string的其他方法
- substr操作
- 改变string的其他方法
- append和replace函数
- 改变string的多种重载函数
- string搜索操作
- 指定在哪里开始搜索
- 逆向搜索
- compare函数
- 数值转换
9.5额外的string操作
标准库string类型定义了大量函数。幸运的是,这些函数使用了重复的模式。由于函数过多,本节初次阅读可能令人心烦,因此读者可能希望快速浏览本节。当你了解string支持哪些类型的操作后,就可以在需要使用一个特定操作时回过头来仔细阅读。
构造string的其他方法
除了构造函数,以及与其他顺序容器相同的构造函数外,string类型还支持另外三个构造函数,如表9.11所示。
表9.11:构造string的其他方法
n、len2和pos2都是无符号值 | |
strings(cp,n) | s是cp指向的数组中前n个字符的拷贝。此数组至少应该包含n个字符 |
strings(s2,pos2) | s是strings2从下标pos2开始的字符的拷贝。若pos2>s2.size(),构造函数的行为未定义 |
strings(s2,pos2,1en2) | s是strings2从下标pos2开始len2个字符的拷贝。若pos2>s2.size(),构造函数的行为未定义。不管len2的值是多少,构造函数至多拷贝s2.size()-pos2个字符 |
这些构造函数接受一个string或一个const char*参数,还接受(可选的)指定拷贝多少个字符的参数。当我们传递给它们的是一个string时,还可以给定一个下标来指出从哪里开始拷贝:
const char*cp = "Hello World!!";//以空字符结束的数组
char noNull[]={'H', 'i'}; //不是以空字符结束
string s1(cp);//拷贝cp中的字符直到遇到空字符;s1==“Hello world!!"
string s2(noNul1,2);//从noNul1拷贝两个字符;s2==“Hi"
string s3(noNul1);//未定义:noNul1不是以空字符结束
string s4(cp+6,5);//从cp[6]开始拷贝5个字符;s4==“World"
string s5(s1,6,5);//从s1[6]开始拷贝5个字符;s5==“World"
string s6(s1,6); //从s1[6]开始拷贝,直至s1结尾; s6==“World”
string s7(s1,6,20);//正确,只拷贝到s1末尾;s7==“World!!”
string s8(s1,16); //抛出一个out_of_range异常
通常当我们从一个const char*创建string时,指针指向的数组必须以空字符结尾,拷贝操作遇到穿字符时停止。如果我们还传递给构造函数一个计数值,数组就不必以空字符结尾。如果我们未传递计数值且数组也未以宇字符结尾,或者给定计数值大于数组大小,则构造函数的行为是未定义的。
当从一个string拷贝字符时,我们可以提供一个可选的开始位置和一个计数值。开始位置必须小于或等于给定的string的大小。如果位置大于size,则构造函数抛出一个out_of_range异常。如果我们传递了一个计数值,则从给定位置开始拷贝这么多个字符。不管我们要求拷贝多少个字符,标准库最多拷贝到string结尾,不会更多。
substr操作
substr操作返回一个string,它是原始string的一部分或全部的拷贝。可以传递给substr一个可选的开始位置和计数值:
strings("hello World");
string s2=s.substr(0,5);//s2= hello
string s3=s.substr(6); //s3=wor1d
string s4=s.Substr(6,11); //s3=world
string s5=s.substr(12); //抛出一个out_of_range异常
如果开始位置超过了string的大小,则substr函数抛出一个out_of_range异常。如果开始位置加上计数值大于string的大小,则substr会调整计数值,只拷贝到string的末尾。
表9.12:子字符串操作
s.substr(pos,n) | 返回一个string,包含s中从pos开始的n个字符的拷贝.pos的默认值为0。n的默认值为s.size()-pos,即拷贝从pos开始的所有字符 |
改变string的其他方法
string类型支持顺序容器的赋值运算符以及assign、insert和erase操作。除此之外,它还定义了额外的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,plump Buck";
s.assign(cp,7);//s==“Stately“
s.insert(s.stze(),cp+7);//s==“Stately,Plump Buck“
此处我们首先通过调用assign替换s的内容。我们赋予s的是从cp指向的地址开始的7个字符。要求赋值的字符数必须小于或等于cp指向的数组中的字符数(不包括结尾的空字符)。
接下来在s上调用insert,我们的意图是将字符插入到s[size()]处(不存在的元素之前的位置。在此例中,我们将cp开始的7个字符〔(至多到结尾空字符之前)拷贝到s中。
我们也可以指定将来自其他string或子字符串的字符插入到当前string中或赋予当前string:
string s="some string",s2="some other string";
s.insert(0,s2);//在s中位置0之前插入s2的拷贝
//在s[0]之前撒入s2中s2[0]开始的s2.size()个字符
s.insert(0, s2,0,s2.stze());
append和replace函数
string类定义了两个额外的成员函数:append和replace,这两个函数可以改变string的内容。表9.13描述了这两个函数的功能。append操作是在string末尾进行插入操作的一种简写形式:
string s("C++Primer"),s2=s;//将s和s2初始化为“C++Primer“
s.insert(s.size(),"4th Ed.");//s==“C++Primer4thEd“
S2.append("4th Ed.")}//等价方法:将"4th Ed."追加到s2;s==s2
rep1lace操作是调用erase和insert的一种简写形式:
```cpp
//将“4th“替换为“5th“的等价方法
s.erase(11,3);//s==“C++Primer.“
s.insert(11,"5thn");//s==“C++Primer5thEd.“
//从位置11开始,删除3个字符并插入“5thn
s2.replace(11,3,"5th");//等价方法:s==s2
此例中调用replace时,插入的文本总好与删除的文本一样长。这不是必须的,可以插入一个更长或更短的string:
s.replace(11,3,"Fifth");//s==“C++Primer Fifth Ed.“
在此调用中,删除了3个字符,但在其位置插入了5个新字符。
表9.13:修改string的操作
s.insert(pos ,arg3) | 在pos之前插入args指定的字符。pos可以是一个下标或一个迭代器。接受下标的版本返回一个指向s的引用,接受迭代器的版本返回指向第一个插入字符的迭代器 |
s.erase(pos ,len) | 删除从位置pos开始的len个字符。如果len被省略,则删除从pos开始直至s末尾的所有字符。返回一个指向s的引用 |
s.assign(args) | 将s中的字符替换为args指定的字符。返回一个指向s的引用 |
s.append(args) | 将args追加到s。返回一个指向s的引用 |
s.replace(range,ar&) | 删除s中范围range内的字符,替换为args指定的守符。range或者是一个下标和一个长度,或者是一对指向s的迭代器。返回一个指向s的引用 |
ags可以是下列形式之一;append和assign可以使用所有形式。str不能与s相同,迭代器b和e不能指向s。 | |
str | 字符串str |
str,pos,len | str中从pos开始最多len个字 |
cp,len | 从cp指向的字符数组的前最多len个字符 |
cp | 指向的以空字符结尾的字符数组 |
n,c | n个字符c |
b,e | 迭代器b和e指定的范围内的字符 |
初始化列表 | 花括号包围的,以逗号分隔的字符列表 |
replace和insert所允许的arg形式依赖于range和pos是如何指定的.
replace | replace | insert | insert | aygs可以是 |
(pos,len,args) | (b,e,args) | (pos,arg) | (iter,args) | |
是 | 是 | 是 | 否 | str |
是 | 否 | 是 | 否 | str |
是 | 否 | 是 | 否 | str,pos,len |
是 | 是 | 是 | 否 | cp,len |
是 | 是 | 否 | 否 | cp |
是 | 是 | 是 | 是 | n,c |
否 | 是 | 否 | 是 | b2,e2 |
否 | 是 | 否 | 是 | 初始化列表 |
改变string的多种重载函数
表9.13列出的append、assign、insert和replace函数有多个重载版本。根据我们如何指定要添加的字符和string中被替换的部分,这些函数的参数有不同版本。幸运的是,这些函数有共同的接口。
assign和append函数无须指定要替换string中哪个部分,assign总是替换string中的所有内容,append总是将新字符追加到string末尾。
replace函数提供了两种指定删除元素范围的方式。可以通过一个位置和一个长度来指定范围,也可以通过一个迭代器范围来指定。insert函数允许我们用两种方式指定插入点:用一个下标或一个迭代器。在两种情况下,新元素都会插入到给定下标(或迭代器)之前的位置。
可以用好几种方式来指定要添加到string中的字符。新字符可以来自于另一个string,来自于一个字符指针(指向的字符数组),来自于一个花括号包围的字符列表,或者是一个字符和一个计数值。当字符来自于一个string或一个字符指针时,我们可以传递一个额外的参数来控制是拷贝部分还是全部字符。
并不是每个函数都支持所有形式的参数。例如,insert就不支持下标和初始化列表参数。类似的,如果我们希望用迭代器指定插入点,就不能用字符指针指定新字符的来源。
string搜索操作
string类提供了6个不同的搜索函数,每个函数都有4个重载版本。表9.14描述了这些搜索成员函数及其参数。每个搜索操作都返回一个string::size_type值,表示匹配发生位置的下标。如果搜索失败,则返回一个名为string::npos的static成员。标准库将npos定义为一个conststring::size_type类型,并初始化为值-1。由于npos是一个unsigned类型,此初始值意味着npos等于任何
string最大的可能大小。
find函数完成最简单的搜索。它查找参数指定的字符串,若找到,则返回第一个匹配位置的下标,否则返回npos:
string name("AnnaBel1e")
auto pos1=name.find("Anna"); //pos1==0
这段程序返回0,即子字符"Anna"在"AnnaBelle"中第一次出现的下标。
搜索(以及其他string操作)是大小写敏感的。当在string中查找子字符串时,要注意大小写:
string lowercase("annabelle");
pos1 = lowercase.find("Anna");//pos1==npos
这段代码会将pos1置为npos,因为Anna与anna不匹配。一个更复杂一些的问题是查找与给定字符串中任何一个字符匹配的位置。例如,下面代码定位name中的第一个数字:
string numbers("0123456789"),name("r2d2");
//返回1,即,name中第一个数字的下标
auto pos=name.find_first_of(numbers);
如果是要搜索第一个不在参数中的字符,我们应该调用find_first_not_of。例如,为了搜索一个string中第一个非数字字符,可以这样做:
string dept("03714p3");
//返回5一一字符'p'的下标
auto pos= dept.find_first_not_of(numbers);
表9.14:string搜索操作
搜索操作返回指定字符出现的下标,如果未找到则返回npos。 | |
s.find(args) | 查找s中args第一次出现的位置 |
s.rfind(args) | 查找s中args最后一次出现的位置 |
s.find_first_of(aregs) | 在s中查找args中任何一个字符第一次出现的位置。 |
s.find_1ast_of(args) | 在s中查找args中任何一个字符最后一次出现的位置 |
s.find_first_not_of(args) | 在s中查找第一个不在args中的字符 |
s.find_1ast_not_of(args) | 在s中查找最后一个不在args中的字符 |
args必须是以下形式之一 | |
c,pos | 从s中位置pos开始查找字符c。pos默认为0 |
s2,pos | 从s中位置pos开始查找守符串s2。pos默认为0 |
cp,pos | 从s中位置pos开始查找指针cp指向的以空字符结尾的C风格守符串.pos默认为0 |
cp,pos,n | 从s中位置pos开始查找指针cp指向的数组的前n个字符。pos和n 无默认值 |
指定在哪里开始搜索
我们可以传递给find操作一个可选的开始位置。这个可选的参数指出从哪个位置开始进行搜索。默认情况下,此位置被置为0。一种常见的程序设计模式是用这个可选参数在字符串中循环地搜索子字符串出现的所有位置:
string::size_type pos=0;
//每步循环查找name中下一个数
while((pos=name.find_first_of(numbers,pos))!=string::npos){
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("Missitssippi");
auto first_pos=river.find("is");//返回1
auto last_pos=river.rfind("is");//返回4
find返回下标1,表示第一个“is“的位置,而rfind返回下标4,表示最后一个“is“的位置。
类似的,find_last函数的功能与find_first函数相似,只是它们返回最后一个而不是第一个匹配:
- find_last_of搜索与给定string中任何一个字符匹配的最后一个字符。
- find_1ast_not_of搜索最后一个不出现在给定string中的字符。
每个操作都接受一个可选的第二参数,可用来指出从什么位置开始搜索。
compare函数
除了关系运算符外,标准库string类型还提供了一组compare函数,这些函数与C标准库的stremp函数(参见3.5.4节,第109页)很相似。类似strcmp,根据s是等于、大于还是小于参数指定的字符丨,s.compare返回0、正数或负数。
如表9.15所示,compare有6个版本。根据我们是要比较两个stritng还是一个string与一个字符数组,参数各有不同。在这两种情况下,都可以比较整个或一部分字符串。
表9.15: s.compare的几种参数形式
s2 | 比较s和s2 |
pos1,n1,s2 | 将s中从pos1开始的n1个字符与s2进行比较 |
pos1,n1,s2,pos2,n2 | 将s中从pos1开始的n1个字符与s2中从pos2开始的n2个字符进行比较 |
cp | 比较s与cp指向的以空字符结尾的字符数组 |
pos1,n1,cp | 将s中从pos1开始的nl个字符与cp指向的以空字符结尾的字符数组进行比较 |
pos1,n1,cP,n2 | 将s中从pos1开始的nl个字符与指针cp指向的地址开始的n2个字符进行比较 |
数值转换
字符串中常常包含表示数值的字符。例如,我们用两个字符的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转换为字符表示形式
double d=stod(s);//将字符串s转换为浮点数
此例中我们调用to_string将42转换为其对应的string表示,然后调用stod将此
string转换为浮点值。
要转换为数值的string中第一个非空白符必须是数值中可能出现的字符:
string s2 = "pi=3.1401";
//转换s中以数字开始的第一个二串,结果d=3.14
d=stod(s2.substr(s2.find_first_of("+-.0123456789")));
在这个stod调用中,我们调用了find_first_of来获得s中第一个可能是数值的一部分的字符的位置。我们将s中从此位置开始的子串传递给stod。stod函数读取此参数,处理其中的孙符,目至遇到不可能是数值的一部分的字符。然后它就将找到的这个数值的字符串表示形式转换为对应的双精度浮点值。
string参数中第一个非宇白符必须是符号(+或-)或数字。它可以以0x或0X开头来表示十六进制数。对那些将字符串转换为浮点值的函数,string参数也可以以小数点(.)开头,并可以包含e或E来表示指数部分。对于那些将宇符串转换为整型值的函数,根据基数不同,string参数可以包含字母字符,对应大于数字9的数。
如果string不能转换为一个数值,这些函数抛出一个invalid_argument如果转换得到的数值无法用任何类型来表示,则抛出一个out_of_range异常。
表9.16:string和数值之间的转换
to_string(val) | -组重载函数,返回数值val的string表示。val可以是任何算术类型。对每个浮点类型和int或更大的整型,都有相应版本的to_string。与往常一样,小整型会被提升 |
stoi(s,p,b) | 返回s的起始子串(表示整数内容)的数值,返回值类型分别是int |
stol(s,p, b)long、unsigned long、long long、unsigned long long。 | |
stoul(s,p,b) | 表示转换所用的基数,默认值为10。p是size_t指针,用来保存 |
stoll(s,p,d) | s中第一个非数值字符的下标,p默认为0,即,函数不保存下标 |
stoul1(s,p,d) | |
stof(s,p) | 返回s的起始子串(表示浮点数内容)的数值,返回值类型分别是 |
stod(s,p) | float、double或longdouble。参数p的作用与整数转换函数 |
stold(s,p) | 中一样 |