string表示可变长的字符序列,vector存放的是某种给定类型对象的可变长序列
os<<s 将s写到输出流os当中,返回os
is>>s 从is中读取字符串赋给s,字符串以空白分隔,返回is
getline(is,s) 从is中读取一行赋给s,返回is
auto len = len.size(); //len的类型是string::size_type
由于size函数返回的是一个无符号整型数,因此切记,如果在表达式中混用了带符号数和无符号数
将可能产生意想不到的结果。例如,假设n是一个具有负值的int,则表达式s.size()<n的判断结果几乎
肯定是true。这是因为负值n会自动地转换成一个比较大的无符号值。
Tip:如果一条表达式中已经有了size()函数就不要再使用int了,这样可以避免混用int和unsigned可能带来的问题。
相等性运算符(==和!=)分别检验两个string对象相等或不相等,string对象相等意味着它们的长度相同而且
所包含的字符也全都完全相同。关系运算符<=,<,>,>=分别检验一个string对象是否小于,小于等于,大于,
大于等于另外一个string对象。上述这些运算符都依照(大小写敏感的)字典顺序:
1.如果两个string对象的长度不同,而且较短string对象的每个字符都与较长string对象对应位置上的字符相同,
就说较短string对象对应位置上的字符相同,就说较短string对象小于较长string对象。
2.如果两个string对象在某些对应的位置不一致,则string对象比较的结果其实是string对象中第一对相异字符
比较的结果。
下面是string对象比较的一个示例:
string str = "Hello";
string phrase = "Hello World";
string slang = "Hiya";
根据规则1可判断,对象str小于对象phrase;根据规则2可判断,对象slang既大于str也大于phrase
字面值和string对象相加
即使一种类型并非所需,我们也可以使用它,不过前提是该种类型可以自动转换成所需的类型。因为标准库
允许把字符字面值和字符串字面值转换成string对象,所以在需要string对象的地方就可以使用这两种字面值
来替代。
string s1 = "hello",s2 = "world"; //在s1和s2中都没有标点符号
string s3 = s1 + "," + s2 + ‘\n‘;
当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符(+)的两侧
运算对象至少有一个是string:
string s4 = s1 + ","; //正确:把一个string对象和一个字面值相加
string s5 = "hello" + ","; //错误:两个运算对象都不是string
//正确:每个加法运算符都有一个运算对象时string
string s6 = s1 + "," + "world";
string s7 = "hello" + "," + s2; //错误:不能把字面值直接相加
因为某些历史原因,也为了与C兼容,所以C++语言中的字符串字面值并不是标准库类型string
的对象。切记,字符串字面值与string是不同的类型
处理string对象中的字符
cctype头文件中的函数
isalnum(c) 当c是字母或数字时为真
isalpha(c) 当c是字母时为真
iscntrl(c) 当c是控制字符时为真
isdigital(c) 当c是数字时为真
isgraph(c) 当c不是空格但可打印时为真
islower(c) 当c是小写字母时为真
isprint(c) 当c是可打印字符时为真(即c是空格或c具有可视形式)
ispunct(c) 当c是标点符号时为真(即c不是控制字符,数字,字母,可打印空白中的一种)
isspace(c) 当c是空白时为真(即c是空格,横向制表符,纵向制表符,回车符,换行符,进纸符的一种)
isupper(c) 当c是大写字母时为真
isxdigit(c) 当c是十六进制数字时为真
tolower(c) 如果c是大写字母,输出对应的小写字母;否则原样输出c
toupper(c) 如果c是小写字母,输出对应的大写字母;否则原样输出c
建议:使用C++版本的c标准库头文件
C++标准库中除了定义C++语言特有的功能外,也兼容了c语言的标准库。C语言的头文件形如name.h。
C++则将这些文件命名为cname.也就是去掉了.h后缀,而在文件名name之前添加了字母c,这里c表示这是
一个属于C语言标准库的头文件。
因此,cctype头文件和ctype.h头文件的内容是一样的,只不过从命名规范上来讲更符合C++语言的
要求。特别的,在名为cname的头文件中定义的名字从属于命名空间std,而定义在名为.h的头文件中的则
不然。
一般来说,C++程序使用命名为cname的头文件而不使用name.h的形式,标准库中的名字总能在命名
空间std中找到。如果使用.h形式的头文件,程序员就不得不时刻牢记哪些是从C语言那儿继承过来的,
哪些又是C++语言所独有的。
处理每个字符?使用基于范围的for语句
如果想对string对象中的每个字符做点儿什么操作,目前最好的办法是使用C++11新标准提供的一种语句:
范围for(range for)语句。这种语句遍历给定序列中每个元素并对序列中的每个值进行某种操作,其语法形式是:
for (declaration : expression)
statement
其中,expression部分是一个对象,用于表示一个序列。declaration部分负责定义一个变量,该变量将被用于访问
序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值。
一个string对象表示一个字符的序列,因此string对象可以作为范围for语句中的expression部分。举个例子,
把string对象中的字符每行一个输出出来:
string str("some thing");
//每行输出str中的一个字符
for (auto c : str) //对于str中的每个字符
cout << c << endl; //输出当前字符,后面紧跟一个换行符
for循环把变量c和str联系了起来,其中我们定义循环控制变量的方式与定义任意一个普通变量
是一样的。此例中,通过使用auto关键字让编译器来决定变量c的类型,这里c的类型是char.每次迭代,
str的下一个字符被拷贝给c,因此该循环可以读作“对于字符串str中的每个字符c,”执行某某操作。此例中
的“某某操作”即输出一个字符,然后换行。
举个稍微复杂的例子,使用范围for语句和ispunct函数统计string对象中标点符号的个数:
string s("Hello World!!!");
//punct_cnt的类型和s.size的返回类型一样;
decltype(s.size()) punct_cnt = 0;
//统计s中标点符号的数量
for (auto c : s) //对于s中的每个字符
if (ispunct(c)) //如果该字符是标点符号
++punct_cnt; //将标点符号计数值加1
cout << punct_cnt
<< "punctuation characters in " << s << endl;
程序的输出结果将是:
3 punctuation characters in Hello World!!!
这里我们使用decltype关键字声明计数变量punct_cnt,它的类型是s.size函数返回值的类型,也就是
string::size_type.使用范围for语句处理string对象中的每个字符并检查其是否是标点符号。如果是,
使用递增运算符给计数变量加1.最后,待范围for语句结束后输出统计结果。
使用范围for语句改变字符串中的字符
如果想要改变string对象中字符的值,必须把循环变量定义成引用类型。记住,所谓引用只是给对象
的一个别名,因此当使用引用作为循环控制变量时,这个变量实际上被依次绑定到了序列的每个元素上。
使用这个引用,我们就能改变它绑定的字符。
新的例子不再是统计标点符号的个数了,假设我们想要把字符串改写为大写字母的形式。为了做到
这一点可以使用标准库函数toupper,该函数接收一个字符,然后输出其对应的大写形式。这样,为了
把整个string对象转换成大写,只要对其中的每个字符调用toupper函数并将结果再赋给原字符就可以了。
string s("Hello World!!!");
//转换成大写形式
for (autp &c : s) //对于s中的每个字符(注意:c是引用)
c = toupper(c); //c是一个引用,因此赋值语句将改变s中字符的值
cout << s << endl;
上述代码的输出结果将是:
HELLO WORLD!!!
每次迭代时,变量c引用string对象s的下一个字符,赋值给c也就是在改变s中对应字符的值。因此当执行
下面的语句时,
c = toupper(c); //c是一个引用,因此赋值语句将改变s中字符的值
实际上改变了c绑定的字符的值。整个循环结束后,str中的所有字符都变成了大写形式
只处理一部分字符?
如果要处理string对象中的每一个字符,使用范围for语句是个好主意。然而,有时我们需要访问的只是其
中一个字符,或者访问多个字符但遇到某个条件就要停下来。例如,同样是将字符改为大写形式,不过新的
要求不再是对整个字符串这样做。而仅仅把string对象中的第一个字母或第一个单词大写化。
要想访问string对象中的单个字符有两种方式:一种是使用下标,另一种是使用迭代器。
下标运算符([])接收的输入参数是string::size_type类型的值,这个参数表示要访问的字符的位置;返
回值是该位置上的引用。
string对象的下标从0计起。如果string对象s至少包含两个字符,则s[0]是第一个字符,s[1]是第二个字
符,s[s.size()-1]是最后一个字符。
Note:string对象的下标必须大于等于0而小于s.size()
使用超出此范围的下标将引发不可预知的结果,以此推断,使用下标访问空string也会引发不可预知的结果。
下标的值称作“下标”或“索引”,任何表达式只要它的值是一个整型值就能作为索引。不过,如果某个索引是
带符号类型的值将自动转换成由string::size_type表达的无符号类型
下面的程序使用下标运算符输出string对象中的第一个字符:
if (!s.empty()) //确保确实有字符需要输出
cout << s[0] << endl; //输出s的第一个字符
在访问指定字符之前,首先检查s是否为空。其实不管什么时候只要对string对象使用了下标,都要确认那个
位置上确实有值。如果s为空,则s[0]的结果将是未定义的。
只要字符串不是常量,就能为下标运算符返回的字符赋新值。例如,下面的程序将字符串的首字符改成了大写
形式:
string s("some string");
if(!s.empty()) //确保s[0]的位置确实有字符
s[0] = toupper(s[0]); //为s的第一个字符赋一个新值
程序的输出结果将是:
Some string
使用下标执行迭代
另一个例子是把s的第一个词改成大写形式:
//依次处理s中的字符直至我们处理完全部字符或者遇到一个空白
for (decltype(s.size()) index = 0;
index != s.size() && !isspace(s[index]); ++ index)
s[index] = toupper(s[index]); //将当前字符改成大写形式
程序的输出结果将是:
SOME string
在上诉程序中,for循环使用变量index作为s的下标,index的类型是由decltype关键字决定的。
首先把index初始化为0,这样第一次迭代就会从s的首字符开始;之后每次迭代将index加1以得
到s的下一个字符。循环体负责将当前字母改写为大写形式。
for语句的条件部分涉及一点新知识,该条件使用了逻辑与运算符(&&)。如果参与运算的
两个运算对象都为真,则逻辑与结果为真;否则结果为假。对这个运算符来说,最重要的一点是,
C++语言规定只有当左侧运算对象为真时才会检查右侧运算对象的情况。如此例所示,这条规定
确保了只有下标取值在合理范围之内时才会真的用此下标访问字符串。也就是说,只有在index
达到s.size()之前才会执行s[index].随着index的增加,它永远不可能超过s.size()的值,所以
可以确保index比s.size()小。
提示:注意检查下标的合法性
使用下标 时必须确保其在合理范围之内,也就是说,下标必须大于等于0而小于字符串的size()
的值。一种简便易行的方法是,总是设下标的类型为string::size_type,因为此类型是无符号数,
可以确保下标不会小于0.此时,代码只需保证下标小于size()的值就可以了。
Tip: C++标准并不要求标准库检查下标是否合法。一旦使用了一个超出范围的下标,就会产生
不可预知的结果。
使用下标执行随机访问
在之前的示例中,我们让字符串的下标每次加1从而按顺序把所有字符改写成了大写形式。其实
也能通过计算得到某个下标值,然后直接获取对应位置的字符,并不是每次都得从前往后依次访问。
例如,想要编写一个程序把0到15之间的十进制数转换成对应的十六进制形式,只需初始化一个
字符串令其存放16个十六进制“数字”:
const string hexdigits = "0123456789ABCDEF"; //可能的十六进制数字
cout << "Enter a series of number between 0 and 15"
<< "separated by apaces. Hit ENTER when finished: "
<< endl;
string result; //用于保存十六进制的字符串
string::size_type n; //用于保存从输入流读取的数
while (cin >> n)
if (n < hexdigits.size()) //忽略无效输入
result += hexdigits[n]; //得到对应的十六进制数字
cout << "Your hex number is: " << result << endl;
假设输入的内容如下:
12 0 5 15 8 15
程序的输出结果将是:
Your hex number is: C05F8F
上述程序的执行过程是这样的:首先初始化变量hexdigits令其存放从0到F的十六
进制数字,注意我们把hexdigits声明成了常量,这是因为在后面的程序中不打算
再改变它的值。在循环内部使用输入值n作为hexdigits的下标,hexdigits[n]的
值就是hexdigits内位置n处的字符。例如,如果n是15,则结果是F;如果n是12,则
结果是C,以此类推。把得到的十六进制数字添加到result内,最后一并输出。
无论何时用到字符串的下标,都应该注意检查其合法性。在上面的程序中,下标
n是string::size_type类型,也就是无符号类型,所以n可以确保大于或等于0。在实际
使用时,还需检查n是否小于hexdigits的长度。