标准库类型string表示可变长的字符序列,使用string必须包含头文件string文件。作为标准库一部分,string定义在命名空间std中。
#include <string>
using std::string;
C++标准一方面对库的类型所提供的操作做来详细规定,另一方面也对库的实现者提出来性能上的要求。因此,标准库对于一般应用场合来说有足够的效率。
1.定义和初始化string对象
string s1; //默认初始化,s1是一个空的字符串
string s2 = s1;//s2是s1的副本
string s3 = "hello"; //s3是字符串字面值的副本
string s4(10, 'c'); //s4的值是ccccccccc
string s5(s1); //s5是s1的副本
如果使用等号=初始化一个变量,实际执行的是拷贝初始化,编译器把等号右侧的初始值拷贝到新创建的对象中去。如果不使用等号则是直接初始化。
当初始值只有一个时,使用拷贝初始化和使用直接初始化都可以。如果像上面s4那样要用到多个值,一般来说只能用到直接初始化。
对于多个值初始化的情况,非要用拷贝初始化也不是不可以,需要显示的创建一个临时对象用来拷贝:
string temp(10, 'c');
string s6 = temp;
尽管s6的语句合法,但是和s4比起来,可读性差,也没有任何优势。所以对于多个值的初始化一般使用直接初始化。
2.string对象操作
操作 | 描述 |
---|---|
os<<s | 将s写到输出流os当中,返回os |
is>>s | 从is中读取字符串到s,字符串以空格分割,返回is |
getline(is, s) | 从is中读取一行赋给is,返回is |
s.empty() | s为空返回true,否则返回false |
s.size() | 返回s中字符串的个数 |
s[n] | 返回s中第n个字符的引用,n从0开始 |
s1+s2 | 返回s1+s2连接的结果 |
s1=s2 | 用s2的副本替代s1中原来的字符 |
s1==s2 | 判断s1和s2是否相等,如果含有完全相同的字符则相等,否则不相等 |
s1!=s2 | 判断s1和s2是否不相等,如果含有完全相同的字符则相等 ,否则不相等 |
<,<=,>,>= | 利用字符在字典中的顺序进行比较,对大小写敏感 |
对于s.size()来说,返回unsigned似乎合情合理。但size返回的其实是string::size_type类型的值。它是一个无符号类型,且能存下任何string对象的大小。由于size返回的是一个无符号类型,因此在表达式中混用了带符合和无符号可能会产生意想不到的结果;
unsigned int n1 = 2;
unsigned int n2 = -2;
string s("hello");
s.size() > n1; //结果为true,两边都是无符号比较
s.size() > n2; //结果为false, n2回自动转换为比较大的无符号类型
字面值和字符串相加
当把string对象和字符串字面值以及字符串字面值混在一条语句中使用时,必须确保每个加法运算的两侧的运算对象至少有一个是string:
string s1 = "hello";
string s2 = s1 + ","; //正确,把一个字符串和字面值相加
string s3 = "hello" + ","; //错误,两个运算都对象都不是string
string s4 = s1 + "," + "world"; //正确,每个加法运算符都有一个string对象
string s5 = "hello" + "," + s1; //错误,第一个加号两边都不是string对象
几个加号连续加的情况可以按在从左往右的顺序依次相加,相加的结果作为下一个加号的左侧运算对象。因此s4中s1+","得到的是string,然后和“world”相加,两次加法运算都至少有一个string对象。s5中第一个加号两侧都没有string对象,因此是非法的。
由于历史原因,也为来兼容C,所以C++字符串字面值并不是标准库string对象,字符串字面值和string是不同的类型。
3.处理string对象中的字符
cctype头文件中定义来一组标准库函数用来处理sting对象中的字符:
函数 | 含义 |
---|---|
isalnum© | 判断c是否是字母或数字 |
isalpha© | 判断c是否是字母 |
iscntrl© | 判断c是否是控制字符 |
isdigit© | 判断c是否是数字 |
isgraph© | 当c不是空格但可以打印时为真 |
islower© | 判断c是否是小写字母 |
isprint© | 当c是可打印字符时为真 |
ispunct© | 当c是标点符号时为真 |
isspace© | 判断c是否是空白,即c是空格、横向制表符、纵向制表符、回车符、换行符、进纸符中的一种 |
issupper© | 判断c是否是大写字母 |
isxdigit© | 判断c是否是十六进制数字 |
tolower© | 将c转化为小写字母 |
toupper© | 将c转化为大写字母 |
建议使用C++标准的头文件
C语言的头文件形如name.h,C++则将这些文件命名为cname,也就是去掉了后缀.h,而在name之前加了c。cname的头文件从属于std命名空间,而.h开头的则不然。一般来说,标准库中的名字总能在命名空间std中找到。如果使用了.h的头文件,则需要时刻记得哪些是从C语言继承过来的,哪些又是C++独有的。
使用基于范围for语句处理string每个字符
C++11新标准提供了一种新的语句:范围for语句,这种语句遍历指定序列中的每个元素并对序列中的每个元素执行某种操作,形式如下:
for(declaration : expression)
statement
其中expression部分是一个对象,用于表示一个序列。declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素。以遍历输出string对象每个字符为例:
string s1("hello");
for(auto c : s1) //这里使用auto自动推导出c的类型,c的类型是s1的基础元素类型
{
cout << c << endl; //输出s1中的每个字符
}
使用范围for语句改变字符串中的字符
如果想要改变string对象中的字符的值,必须把循环变量定义成引用类型。使用引用作为循环控制变量时,这个变量实际上被依次绑定到了序列的每个元素上。以改变string对象将大写字母全部改为小写字母为例:
string s2("Hello,World");
for(auto &c : s2)
{
c = tolower(c); //c是引用,因此赋值语句将改变s中字符的值
cout << c << endl; //输出s1中的每个字符
}
使用下标访问string中的字符
下标运算符[]接收的输入参数是string::size_type的类型值,这个参数表示要访问的字符的位置,从0开始,返回值是该位置上的字符的引用。
string对象的下标必须大于等于0而小于s.size(),使用超出此范围的下标将引发不可预知的后果。可以将下标的类型设置成string::size_type类型,这样可以确保下标不会小于0。此时只要确保下标小于s.size()的值就可以了。