String 类和 STL (Standard Template Library)
一. string 类
很多应用程序都需要处理字符串。C语言在string.h(在++中为cstring)中提供了一系列的字符串函数,很多早期的C++实现为处理字符串提供了自己的类。
string类是由头文件string支持的(以头文件string.h和cstring支持对C风格字符串进行操纵的C库字符串函数,但不支持string类)。要使用类,关键在于知道它的公有接口,而string类包含大量的方法,其中包括了若干构造函数,用于将字符串赋给变量、合并字符串、比较字符串和访问各个元素的重载运算符以及用于在字符串中查找字符和子字符串的工具等。以string类包含的内容很多。
1. 构造字符串
先来看string的构造函数。毕竟,对于类而言,最重要的内容之一是,有哪些方法可用于创建其对象。程序清单1使用了string的7个构造函数(用ctor标识,这是传统C++中构造函数的缩写)。表1简要地描述了这些构造函数,它首先按顺序简要描述了程序清单1使用的7个构造函数,然后列出了C++11新增的两个构造函数。使用构造函数时都进行了简化,即隐藏了这样一个事实:string实际上是模板具体化basic_string<char>的一个typedef,同时省略了与内存管理相关的参数。 size_type是一个依赖于实现的整型,是在头文件string中定义的。string类将
string::npos定义为字符串的最大长度,通常为unsigned int的最大值。
以表格中使用缩写
NBTS
(null终止string)来表示以空字符结束的字符串一一传统的C字符串。
表1 string类的构造函数
构 造 函 数
描述
string(const char * s)
将string对象初始化为s指向的NBTS
string(size_type n, char c)
创建一个包含n个元素的string对象,其中每个元素都被初始化为字符c
string(const string & str)
将一个string对象初始化为string对象str(复制构造函数)
string()
创建一个默认的string对象,长度为0(默认构造函数)
string(const char * s, size_type n)
将string对象初始化为s指向的NBTS的前n个字符,即使超过了NBTS结尾
template<class Iter>
string(Iter begin, Iter end)将string对象初始化为区间[begin, end)内的字符,其中begin和end的行为就像指针,用于指定位置,范围包括begin在内,但不包括end
string(const string & str, size_type pos, size_type n = npos)
将一个string对象初始化为对象str中从位置pos开始到结尾的字符,或从位置pos开始的n个字符
string(string && str) noexcept
这是C++11新增的,它将一个string对象初始化为string对象str,并可能修改str(移动构造函数)
string(initializer_list<char> il)
这是C++11新增的,它将一个string对象初始化为初始化列表il中的字符
程序清单1 :
#include <iostream> #include <string> // using string constructors int main() { using namespace std; string one("Lottery Winner!"); // ctor #1 cout << one << endl; // overloaded << string two(20, '$'); // ctor #2 cout << two << endl; string three(one); // ctor #3 cout << three << endl; one += " Oops!"; // overloaded += cout << one << endl; two = "Sorry! That was "; //will Clean up the original string in object two three[0] = 'P'; string four; // ctor #4 four = two + three; // overloaded +, = cout << four << endl; char alls[] = "All's well that ends well"; string five(alls,20); // ctor #5 cout << five << "!\n"; string six(alls+6, alls + 10); // ctor #6 cout << six << ", "; string seven(&five[6], &five[10]); // ctor #6 again cout << seven << "...\n"; cout << "Now four is :" << four << endl; string eight(four, 7, 16); // ctor #7 cout << eight << " in motion!" << endl; system("pause"); return 0; }
程序清单1 的输出:
Lottery Winner! $$$$$$$$$$$$$$$$$$$$ Lottery Winner! Lottery Winner! Oops! Sorry! That was Pottery Winner! All's well that ends! well, well... Now four is :Sorry! That was Pottery Winner! That was Pottery in motion! Press any key to continue . . .
2. string类输入
有哪些输入方式可用呢? 对于C风格的字符串, 有3种方式:
char info[100]; cin >> info; // read a word cin.getline(info, 100); // read a line, discard \n cin.get(info, 100); // read a line, leave \n in queue
对于string 对象 有两种方式:
string stuff; cin >> stuff; // read a word getline(cin, stuff); // read a line, discard \n
两个版本的getline() 都有一个可选参数, 用于指定使用什么字符作为读取的边界;
cin.getline(info,100,':'); // read up to :, discard : getline(cin,stuff, ':'); // read up to :, discard :
对于string版本的getline() 能够自动调整目标string 对象的大小, 使之刚好能存储输入的字符:
char fname[10]; string lname; cin >> fname; // could be a problem if input size > 9 characters cin >> lname; // can read a very, very long word cin.getline(fname, 10); // may truncate input getline(cin, fname); // no truncation
自动调整大小的功能让 string版本的getline() 不需要指定要读取多少个字符的参数
string 版本的 getline() 从输入流中读取字符, 并将其放入目标string 中, 直到发生下面几种情况:
- 到达文件尾的输入流的eofbit将被设置,这意味着方法fail()和eof()都将返回true;
- 遇到分界字符(默认为\n) 在这种情况下, 将把分界字符从输入流中删除,但不存储它;
- 读取的字符数达到最大允许值(string::npos和可供分配的内存字节数中较小的一个) 在这种情况下,将设置输入流的failbit,这意味着方法fail()将返回true。
eofbit fail()等与
流状态
相关, 将在
~
C++输入输出和文件
-> 三. 使用cin进行输入 -> 2. 流状态
~
中讲解
3. 使用字符串
字符串比较
string 类对全部6个关系运算符都进行了重载, 如果机器排列序列为
ASCII码
, 那么数字字符 < 大写字符 < 小写字符;
对于每个关系运算符, 都以三种方式被重载, 以便将string对象和 另一个string对象,C风格字符串 进行比较 :#include <iostream> #include <exception> int main() { using namespace std; string snake1("cobra"); string snake2("coaal"); char snake3[20] = "cobra"; if (snake1 < snake2) // operator<(const string &, const string &) { cout << "snake1 < snake 2" << endl; } if (snake1 == snake3) // operator==(const string &, const char *) { cout << "snake1 == snake3" << endl; } if (snake3 != snake2) // operator!=(const char *, const string &) { cout << "snake3 != snake2" << endl; } system("pause"); return 0; }
size() 和 length() 都返回字符串的字符数
length()成员来自较早版本的string类, 而size()则是为STL兼容性而添加的字符串查找
string::npos是字符串可存储的最大字符数, 通常是无符号int或long的最大取值;
表2 重载的find()方法
方 法 原 型
描 述
size_type find(const string & str,
size_type pos = 0)const从字符串的pos位置开始,查找子字符串str。如果找到,则返回该子字符串首次出现时其首字符的索引;否则,返回string :: npos
size_type find(const char * s,
size_type pos = 0)const从字符串的pos位置开始,查找子字符串s。如果找到,则返回该子字符串首次出现时其首字符的索引;否则,返回string :: npos
size_type find(const char * s,
size_type pos, size_type n)从字符串的pos位置开始,查找s的前n个字符组成的子字符串。如果找到,则返回该子字符串首次出现时其首字符的索引;否则,返回string :: npos
size_type find(char ch,
size_type pos = 0)const从字符串的pos位置开始,查找字符ch。如果找到,则返回该字符首次出现的位置;否则,返回string :: npos
string 库还提供了相关的方法:
rfind()、find_first_of()、find_last_of()、find_first_not_of()和find_last_not_of()
,它们的重载函数特征标都与find()方法相同。rfind()方法查找子字符串或字符最后一次出现的位置;find_first_of()方法在字符串中查找参数中任何一个字符首次出现的位置。例如,下面的语句返回 r 在“cobra”中的位置(即索引3),因为这是“hark”中各个字母在“cobra”首次出现的位置:
int where = snake1.find_first_of("hark");
find_last_of()方法的功能与此相同,只是它查找的是最后一次出现的位置。因此,下面的语句返回a在“cobra”中的位置:
int where = snake1.find_last_of("hark");
find_first_not_of()方法在字符串中查找第一个不包含在参数中的字符,因此下面的语句返回c在“cobra”中的位置,因为“hark”中没有c:
int where = snake1.find_first_not_of("hark");
4. 其他string类方法
很多, 就萝莉一些, 再挑几个讲. 其他的用到的时候就知道了
a) =,assign() //赋以新值 b) swap() //交换两个字符串的内容 c) +=,append(),push_back() //在尾部添加字符 d) insert() //插入字符 e) erase() //删除字符 f) clear() //删除全部字符 g) replace() //替换字符 h) + //串联字符串 i) ==,!=,<,<=,>,>=,compare() //比较字符串 j) size(),length() //返回字符数量 k) max_size() //返回字符的可能最大个数 l) empty() //判断字符串是否为空 m) capacity() //返回重新分配之前的字符容量 n) reserve() //保留一定量内存以容纳一定数量的字符 o) [], at() //存取单一字符 at()索引无效时,会抛出out_of_range异常 p) >>,getline() //从stream读取某值 q) << //将谋值写入stream r) copy() //将某值赋值为一个C_string s) c_str() //将内容以C_string返回 不可修改 t) data() //将内容以字符序列指针形式返回 可修改 u) substr() //返回某个子字符串 v)查找函数 w)begin() end() //提供类似STL的迭代器支持 x) rbegin() rend() //逆向迭代器 y) get_allocator() //返回配置器
compare 返回值意义(吾用区间表示法表示):[小于,0,大于] , 0:相等
string s("abcd"); s.compare("abcd"); //返回0 s.compare("dcba"); //返回一个小于0的值 s.compare("ab"); //返回大于0的值 s.compare(s); //相等 //参数1:下标 2:字符个数 3:比较的对象 4:下标 5:字符个数 s.compare(0,2,s,2,2); //用"ab"和"cd"进行比较 小于零 //参数1:下标 2:字符个数 3:比较的对象 4:字符个数 s.compare(1,2,"bcx",2); //用"bc"和"bc"比较。
assign 重新分配
s.assign(str); //字面意思 //参数1:目标 2:下标 3:字符数 s.assign(str,1,3);//如果str是"iamangel" 就是把"ama"赋给字符串 s.assign(str,2,string::npos);//把字符串str从索引值2开始到结尾赋给s s.assign("gaint"); //字面意思 s.assign("nico",5);//把’n’ ‘I’ ‘c’ ‘o’ ‘’赋给字符串 s.assign(5,'x');//把五个x赋给字符串
append 附加
s.append(str); s.append(str,1,3);//不解释了 同前面的函数参数assign的解释 s.append(str,2,string::npos)// s.append("my name is jiayp"); s.append("nico",5); s.append(5,'x'); s.push_back('a');//这个函数只能增加单个字符
insert 插入
s.insert(0,"my name"); s.insert(1, "m"); s.insert(1,str);
replace erase 替换和擦除
s.replace(1,2,"nternationalizatio");//从索引1开始的2个替换成后面的C_string或string对象 s.erase(13);//从索引13开始往后全删除 s.erase(7,5);//从索引7开始往后删5个
substr 返回子字符串(新的string)
s.substr();//返回s的全部内容 s.substr(11);//从索引11往后的子串 s.substr(5,6);//从索引5开始6个字符
copy 复制并替换目标中原有的字符
char str1[20] = "Hello"; char str2[20] {0}; string sl = "World"; //参数1:目标对象 2:要copy的字符数 3:从sl的下标?开始 sl.copy(str1, 5, 2);// cout << str1 << endl;
方法capacity()返回当前分配给字符串的内存块的大小,而reserve()方法让您能够请求增大内存块
#include <iostream> #include <string> int main() { using namespace std; string empty; string small = "bit"; string larger = "Elephants are a girl's best friend"; cout << "Sizes:\n"; cout << "\tempty: " << empty.size() << endl; cout << "\tsmall: " << small.size() << endl; cout << "\tlarger: " << larger.size() << endl; cout << "Capacities:\n"; cout << "\tempty: " << empty.capacity() << endl; cout << "\tsmall: " << small.capacity() << endl; cout << "\tlarger: " << larger.capacity() << endl; empty.reserve(50); cout << "Capacity after empty.reserve(50): " << empty.capacity() << endl; return 0; }
如果您有string对象,但需要C风格字符串,该如何办呢?
string filename; filename.c_str(); //返回c风格字符串
5. 字符串种类
本节将string类看作是基于char类型的。事实上,正如前面指出的,string库实际上是基于一个模板类的:
template<class charT, class traits = char _traits<charT>, class Allocator = allocator<charT> > basic_string {...};
模板basic_string有4个具体化(特化),每个具体化都有一个typedef名称:
typedef basic_string<char> string; typedef basic_string<wchar_t> wstring; typedef basic_string<char16_t> u16string; // C++11 typedef basic_string<char32_t> u32string ; // C++11
这让您能够使用基于类型wchar_t、char16_t、char32_t和char的字符串。甚至可以开发某种类似字符的类,并对它使用basic_string类模板(只要它满足某些要求)。traits类描述关于选定字符类型的特定情况,如如何对值进行比较。对于wchar_t、char16_t、char32_t和char类型,有预定义的char_traits模板具体化,它们都是traits的默认值。Allocator是一个管理内存分配的类。对于各种字符类型,都有预定义的allocator模板具体化,它们都是默认的。它们使用new和delete。