9.6. 再谈 string 类型
表 9.12 第 3.2 节介绍的 string 操作
除了已经使用过的操作外,string 类型还支持大多数顺序容器操作。在某些方面,可将 string 类型视为字符容器。除了一些特殊操作,string 类型提供与 vector 容器相同的操作。string 类型与 vector 容器不同的是,它不支
持以栈方式操纵容器:在 string 类型中不能使用 front、back 和 pop_back 操
作。
string 支持的容器操作有:
• 表 9.5 列出的 typedef,包括迭代器类型。
• 表 9.2 列出的容器构造函数,但是不包括只需要一个长度参数的构造函数。
• 表 9.7 列出的 vector 容器所提供的添加元素的操作。注意:无论 vector 容器还是 string 类型都不支持 push_front 操作。
• 表 9.8 列出的长度操作。
• 表 9.9 列出的下标和 at 操作;但 string 类型不提供该表列出的back和 front 操作。
• 表 9.6 列出的 begin 和 end 操作。
• 表 9.10 列出的 erase 和 clear 操作;但是 string 类型不入提供pop_back 或 pop_front 操作。
• 表 9.11 列出的赋值操作。
• 与 vector 容器的元素一样,string的字符也是连续存储的。因此,string 类型支持第 9.4 节描述的 capacity 和 reserve 操作。
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
string s("Hiya!");
string::iterator iter = s.begin();
while (iter != s.end())
cout << *iter++ ; // postfix increment: print old value
system ("pause");
return 0;
}
除了共享容器的操作外,string 类型还支持其他本类型特有的操作。
string s1; // s1 is the empty string
string s2(5, 'a'); // s2 == "aaaaa"
string s3(s2); // s3 is a copy of s2
string s4(s3.begin(),
s3.begin() + s3.size() / 2); // s4 == "aa"
除了上述构造函数之外,string 类型还提供了三种其他的方式创建类对象(表 9.13)。在前面的章节中,已经使用过只有一个指针参数的构造函数,该指针指向以空字符结束的字符数组中的第一个元素。另一种构造函数需要一个指向字符数组元素的指针和一个标记要复制多少个字符的计数器作参数。由于该构造函数带有一个计数器,因此数组不必以空字符结束:
char *cp = "Hiya"; // null-terminated array
char c_array[] = "World!!!!"; // null-terminated
char no_null[] = {'H', 'i'}; // not null-terminated
string s1(cp); // s1 == "Hiya"
string s2(c_array, 5); // s2 == "World"
string s3(c_array + 5, 4); // s3 == "!!!!"
string s4(no_null); // runtime error: no_null not null-terminated
string s5(no_null, 2); // ok: s5 == "Hi"
用子串做初始化式
另一对构造函数使用程序员可以在创建 string 对象时将其初始化为另一个 string 对象的子串。
string s6(s1, 2); // s6 == "ya"
string s7(s1, 0, 2); // s7 == "Hi"
string s8(s1, 0, 8); // s8 == "Hiya"
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
string s("hello world");
// return substring of 5 characters starting at position 6
string s2 = s.substr(6, 5); // s2 = world
cout<<s2<<endl;
//the same function
// return substring from position 6 to the end of s
string s3 = s.substr(6); // s3 = world
cout<<s3;
system ("pause");
return 0;
}
append 和 replace函数
append 操作,字符将添加在 string 对象的末尾。而 replace 函数则将这些字符插入到指定位置,从而替换 string 对象中一段已存在的字符。
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
string s("C++ Primer"); // initialize s to "C++ Primer"
s.append(" 3rd Ed."); // s == "C++ Primer 3rd Ed."
cout<<s<<endl;
// equivalent to s.append(" 3rd Ed.")
/* s.insert(s.size(), " 3rd Ed.");
cout<<s<<endl;*/
// starting at position 11, erase 3 characters and then insert "4th"
s.replace(11, 3, "4th"); // s == "C++ Primer 4th Ed."
cout<<s<<endl;
// equivalent way to replace "3rd" by "4th"
s.erase(11, 3); // s == "C++ Primer Ed."
s.insert(11, "4th"); // s == "C++ Primer 4th Ed."
cout<<s<<endl;
system ("pause");
return 0;
}
9.6.4. string 类型的查找操作
string 类提供了 6 种查找函数(表 9.19),每种函数以不同形式的 find命名。这些操作全都返回 string::size_type 类型的值,以下标形式标记查找匹配所发生的位置;或者返回一个名为 string::npos 的特殊值,说明查找没有匹配。string 类将 npos 定义为保证大于任何有效下标的值。
每种查找操作都有 4 个重载版本,每个版本使用不同的参数集合。表 9.20列出了查找操作使用的不同参数形式。基本上,这些操作的不同之处在于查找的到底是单个字符、另一个 string 字符串、C 风格的以空字符结束的字符串,还是用字符数组给出的特定数目的字符集合。
string 类型提供的 find 操作的参数
精确匹配的查找
最简单的查找操作是find 函数,用于寻找实参指定的内容。如果找到的话,则返回第一次匹配的下标值;如果找不到,则返回 npos:
stringname("AnnaBelle");
string::size_type pos1 =name.find("Anna"); // pos1 == 0
默认情况下,find 操作(以及其他处理字符的string 操作)使用内置操作符比较 string 字符串中的字符。因此,这些操作(以及其他 string 操作)都区分字母的大小写。
stringlowercase("annabelle");
pos1 =lowercase.find("Anna"); // pos1 == npos
查找任意字符
如果在查找字符串时希望匹配任意指定的字符,则实现起来稍微复杂一点。例如,下面的程序要在 name 中寻找并定位第一个数字:
string numerics("0123456789");
string name("r2d2");
string::size_type pos = name.find_first_of(numerics);
cout << "found number at index: " << pos
<< " element is " << name[pos] << endl;
在这个例子中,pos 的值被设置为 1
指定查找的起点
程序员可以给 find 操作传递一个可选的起点位置实参,用于指定开始查找的下标位置,该位置实参的默认值为 0。通常的编程模式是使用这个可选的实参循环查找 string 对象中所有的匹配。下面的程序重写了查找“r2d2”的程序,以便找出 name 字符串中出现的所有数字:
string::size_type pos = 0;
// each trip reset pos to the next instance in name
while ((pos = name.find_first_of(numerics, pos))
!= string::npos) {
cout << "found number at index: " << pos
<< " element is " << name[pos] << endl;
++pos; // move to the next character
}
寻找不匹配点
除了寻找匹配的位置外,还可以调用 find_first_not_of 函数查找第一个与实参不匹配的位置。例如,如果要在 string 对象中寻找第一个非数字字符,可以如下编写程序: string numbers("0123456789");
string dept("03714p3");
// returns 5, which is the index to the character 'p'
string::size_type pos = dept.find_first_not_of(numbers);
反向查找
rfind成员函数用于寻找最后一个——也就是是最右边的——指定子串出现的位置: string river("Mississippi");
string::size_type first_pos = river.find("is"); // returns 1
string::size_type last_pos = river.rfind("is"); // returns 4
rfind 函数返回最后一个匹配的位置,返回的位置是从左边数第几个,从0开始。
• find_last_of 函数查找与目标字符串的任意字符匹配的最后一个字符。
• find_last_not_of 函数查找最后一个不能跟目标字符串的任何字符匹配的字符。
9.6.5.字符串比较
相等(==)、不等(!=),以及实现小于或大于(<、<=、>、>=)运算。
字符串比较区分大小写。
操作符逐个字符地进行比较,直到比较到某个位置上,两个 string 对象对应的字符不相同为止。string 对象的整个比较依赖于不相同字符之间的比较。如果要比较的两个 string 对象长度不相同,而且一个 string 对象是另一个 string 对象的子串,则较短的 string 对象小于较长的 string 对象。
compare函数
s1.compare (args);
compare 函数返回下面列出的三种可能值之一:
1. 正数,此时 s1 大于 args 所代表的 string 对象。
2. 负数,此时 s1 小于 args 所代表的 string 对象。
3. 0,此时 s1 恰好等于 args 所代表的 string 对象。