3.2.3 处理string对象中的字符(2)
上述代码的输出结果将是:
- HELLO WORLD!!!
每次迭代时,变量c引用string对象s的下一个字符,赋值给c也就是在改变s中对应字符的值。因此当执行下面的语句时,
- c = toupper(c); // c是一个引用,因此赋值语句将改变s中字符的值
实际上改变了c绑定的字符的值。整个循环结束后,str中的所有字符都变成了大写形式。
只处理一部分字符?
如果要处理string对象中的每一个字符,使用范围for语句是个好主意。然而,有时我们需要访问的只是其中一个字符,或者访问多个字符但遇到某个条件就要停下来。例如,同样是将字符改为大写形式,不过新的要求不再是对整个字符串都这样做,而仅仅把string对象中的第一个字母或第一个单词大写化。
要想访问string对象中的单个字符有两种方式:一种是使用下标,另外一种是使用迭代器,其中关于迭代器的内容将在3.4节(第106页)和第9章中介绍。
下标运算符([ ])接收的输入参数是string::size_type类型的值(参见3.2.2节,第88页),这个参数表示要访问的字符的位置;返回值是该位置上字符的引用。
string对象的下标从0计起。如果string对象s至少包含两个字符,则s[0]是第1个字符、s[1]是第2个字符、s[s.size()-1]是最后一个字符。
string对象的下标必须大于等于0而小于s.size()。
使用超出此范围的下标将引发不可预知的结果,以此推断,使用下标访问
空string也会引发不可预知的结果。
下标的值称作"下标"或"索引",任何表达式只要它的值是一个整型值就能作为索引。不过,如果某个索引是带符号类型的值将自动转换成由string::size_type(参见2.1.2节,第36页)表达的无符号类型。
下面的程序使用下标运算符输出string对象中的第一个字符:
- if (!s.empty()) // 确保确实有字符需要输出
- cout << s[0] << endl; // 输出s的第一个字符
在访问指定字符之前,首先检查s是否为空。其实不管什么时候只要对string对象使用了下标,都要确认在那个位置上确实有值。如果s为空,则s[0]的结果将是未定义的。
只要字符串不是常量(参见2.4节,第59页),就能为下标运算符返回的字符赋新值。例如,下面的程序将字符串的首字符改成了大写形式:
- 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++(www.cppentry.com)语言规定只有当左侧运算对象为真时才会检查右侧运算对象的情况。如此例所示,这条规定确保了只有当下标取值在合理范围之内时才会真的用此下标去访问字符串。也就是说,只有在index达到s.size()之前才会执行s[index]。随着index的增加,它永远也不可能超过s.size()的值,所以可以确保index比s.size()小。