string类在string头文件中,string定义在命名空间std中,若想使用string必须在开头包含如下代码:
#include <string>
using std::string;
定义和初始化string对象
有如下几种方式:
string s1; //默认初始化,s1是一个空串
string s2(s1); //s2是s1的副本
string s2 = s1; //与上相同
string s3("value"); //s3是字面值"value"的副本,除了最后的空字符以外所有字符都被拷贝到新创建的string对象中去
string s3 = "value"; //与上相同
string s4(n, 'c'); //把s4初始化为有连续n个字符c组成的字符串
由上可见,有两种初始化方式,拷贝初始化和直接初始化。使用等号(=)初始化一个变量实际上执行的是拷贝初始化,编译器把等号右侧的初始值拷贝到新创建的对象中去。而不使用等号则指行的是直接初始化。一般用多个值进行初始化时,进行的是直接初始化,也可以通过以下方式使用拷贝初始化:
string s5 = string(10, 'c');
本质上等价于以下方式进行初始化:
string temp = string(10, 'c');
string s5 = temp;
string对象上的操作
输入输出
os << s //将s写入到输出流中,返回os
is >> s //从is中读取字符串赋给s,字符串以空白分隔,返回is
getline(is, s) //从is中读取一行赋给s,返回is
在执行读取操作时,string对象会自动忽略开头的空白(空格符、换行符、制表符等),并从第一个真正的字符开始读起,知道遇到下一处空白为止。
与内置类型的输入输出操作一样,string对象的此类操作也是返回运算符左侧的运算对象作为其结果。因此,多个输入和多个输出可以连在一起。
如果希望在最终得到的字符串中保留输入时的空白字符,应该使用getline函数代替原来的>>运算符。getline函数的参数是一个输入流和一个string对象。函数从给定的流中读入内容,直到遇到换行符为止(换行符也被读了进来),然后将读到的内容存入到string对象中去(不存换行符,实际上被丢弃)。getline函数一遇到换行符就结束读取操作并返回结果,哪怕一开始就是换行符,如果发生该种情况,则所得结果是一个空string。getline函数也会返回其流参数,可以作为判断条件。
有如下程序:
#include <iostream>
#include <string>
using std::string;
using std::cout;
using std::cin;
using std::endl;
int main(int argc, char const *argv[])
{
string line = "hello";
while (getline(cin, line))
cout << line << endl;
}
getline函数会清空原来string对象的内容并将读到的字符串存入string对象中。
当getline读到文件结束标记时结束循环。Windows下输入文件结束标记的方法是Ctrl+Z再回车。经测试发现,该函数仅会在某次输入的开头为Ctrl+Z时终止,如果出现位置不在开头,则程序不会终止。并且忽略Ctrl+Z后面的内容,且按两次回车后才会在屏幕上打印字符串内容,Ctrl+Z显示为乱码。
string的empty和size操作
empty函数根据string对象是否为空返回一个对应的布尔值,若为空则返回真,不为空返回假。使用如下:
string line;
if (line.empty) {
...
}
size函数返回string对象的长度,即string对象中字符的个数。其返回值为一个string::size_type类型的值,该类型在类string中定义,是一个无符号类型的值。在C++11新标准中,允许编译器通过auto或者decltype来推断变量的类型。所以可以有:
string line = "hello";
auto size = line.size();
而不需要了解string::size_type类型的细节。
由于size函数返回的是一个无符号整型数,如果在表达式中混用了带符号数和无符号数可能产生难以预计的后果。假设n是一个负值int,则表达式s.size()
比较
相等性运算符(==和!=)可以检验两个string对象相等或者不相等。
关系运算符(<, <=, >, >=)分别检测两个string对象的大小关系。这些运算符都按照字典顺序,其规则如下:
1.如果两个string对象长度不相等,且较短的string对象的每个字符都与较长string对象对应位置上的字符相同,就说较短对象小于较长对象。
2.如果两个string对象在某些位置上不一致,则string对象比较的结果其实是两个string对象中第一对相异字符比较的结果。
string str = "hello";
string phrase = "hello world";
string slang = "hiya";
对象str小于对象phrase,对象slang大于str和phrase。
相加
两个string对象相加得到了一个新的string对象,其内容是左侧的运算符对象和右侧的运算符对象串接而成。
因为标准库允许把字符字面值和字符串字面值转换成string对象,所以也可以使用字符字面值或者字符串字面值与string对象相加。当把string对象和字符字面值以及字符串字面值混住一条语句中时,必须确保每个加法运算符两侧的对象至少有一个是string类型以便于完成类型转换:
string s1 = "hello";
string s2 = s1 + ", world" + '\n'; //正确,一个string对象和一个字符串字面值以及一个字符字面值相加
string s3 = "hello" + ", "; //错误,两个字符串字面值不可相加
string s4 = s1 + ", " + "world"; //正确,每个加法运算符都有一个对象是string
string s5 = "hello" + ", " + s1; //错误,不能把字面值直接相加
s4初始化过程可如下分组:
string s4 = (s1 + ", ") + "world";
等价于如下过程:
string temp = s1 + ", ";
string s4 = temp + "world";
而s5初始化过程是非法的,其等价过程如下:
string s5 = ("hello" + ", ") + s1;
不能把字面值直接相加。
由于某些历史原因,也为了与C兼容,C++中字符串字面值的类型并不是标准库类型string的对象。字符串字面值与string是不同的类型。
访问string对象上的字符
基于范围的for语句
基于范围的for语句语法形式是:
for (declaration : expression)
statement
expression是一个对象,用于表示一个序列。declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression 部分的下一个元素的值。
如:
string str("hello world");
for (auto c : str) {
cout << c << endl;
}
通过使用auto关键字来让编译器决定变量c的类型,这里是char。
通过将auto定义的变量声明为引用类型可以改变string对象中字符的值。如:
string str = "hello world";
for (auto &c : str) {
toupper(c);
}
关于auto关键字见C++11auto部分内容。toupper为cctype库中的函数。C++标准库中兼容了C语言的标准库,将原有的.h头文件去掉.h之后再其前加上c表示其是属于C语言标准库的头文件。cctype头文件与ctype.h头文件内容一样。
使用下标
下标运算符([])接收的输入参数是string::size_type类型的值,表示要访问的字符的位置,返回该位置上字符的引用。string的下标从0开始到size()-1为止,试图使用超出此范围的下标将引发不可预料的后果,使用下标访问空string对象也会如此。使用下标执行迭代如下所示:
string str = "hello world";
for (decltype(str.size()) index = 0; index != str.size(); index++)
cout << str[index] << endl;
C++标准并不要求标准库检测下标是否合法。一旦使用了超出范围的下标,就会产生不可预料的结果。
基于迭代器的访问
详见迭代器章节。