3.2 标准库类型 string
标准库类型string表示可变长的字符序列,使用string
类型需要包含头文件string
3.2.1 定义和初始化 string 对象
以下是声明定义一个string
对象的例子
string s1; // 默认初始化,s1是一个空字符串
string s2 = s1; // s2是s1的副本
string s3 = "hiya"; // s3的内容是该字符串字面值的副本
string s4(10, 'c'); // s4的内容是cccccccccc
string s5("value");
直接初始化和拷贝初始化
如果使用等号=
初始化一个变量,实际上执行的是拷贝初始化 (copy initalization) ,编译器把等号右侧的初始值拷贝到新创建的对象中去。反之,如果不使用等号,则执行的是直接初始化 (direct initalization)。
3.2.2 string 对象上的操作
读写 string 对象
可以使用 IO 操作符读写string
对象。
注意:在执行读取操作时,string
对象会自动忽略开头的空白(即空格符、换行符、制表符等)并从第一个真正的字符开始读起,直到遇见下一个空白。输出结果中没有任何空白。
读取未知数量的 string 对象

int main(){
string word;
while(cin >> word) // 遇到文件结束符或非法输入则结束
cout << word << endl;
return 0;
}
使用 getline 读取一整行
int main(){
string line;
while(getline(cin, line))
cout << line << endl;
return 0;
}
getline
函数的参数是一个输入流和一个string
对象,函数从给定的输入流中读入内容,直到遇到换行符(读取换行符)为止,然后把所读的内容存入到那个string
对象(不存换行符)中去。
string 的 empty 和 size 操作
empty
函数根据string
对象是否为空返回一个对应的布尔值。
size
函数返回string
对象的长度(即string
对象中字符的个数),可以使用size
指定输出长度的字符
string line;
while(getline(cin, line))
if(line.size() > 80) // 输出超过 80 个字符的行
cout << line << endl;
string::size_type 类型
size
函数返回的是一个string::size_type
类型的值,这个值是一个无符号整数,所以如果一条表达式中已经有了size
函数就不要再使用int
。这样可以避免混用int
和unsigned
可能带来的问题。
比较 string 对象
比较运算符逐一比较string
对象中的字符,对大小写敏感。
-
相等性运算符
==
和!=
校验两个
string
对象相等或不相等,相等意味着string
对象的长度、包含的字符完全相同。 -
关系运算符
<
、<=
、>
、>=
,按字典顺序比较对象的小于、小于等于、大于、大于等于。- 如果两个
string
对象的长度不同,而且较短string
对象的每个字符都和较长string
对象对应位置上的字符相同,就说较短string
对象小于较长string
对象; - 如果两个
string
对象在某些对应的位置上不一致,则string
对象比较的结果其实是string
对象中第一对相异字符比较的结果。
- 如果两个
string 对象赋值
允许把一个string
对象的值赋给另一个string
对象
两个 string 对象相加
两个string
对象相加得到一个新的string
对象,其内容是把左侧的运算对象与右侧的运算对象串接而成。
string s1 = "hello", s2 = " world";
string s3 = s1 + s2; // s3 内容是 "hello world"
s1 += s2; // 等价于 s1 + s2
字面值和 string 对象相加
注意 C++ 语言中的字符串字面值并不是string
的对象,即字符串字面值与string
是不同的类型
当把string
对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符+
的两侧的运算对象至少有一个是string
string s1 = "hello", s2 = "world"; // 在s1和s2中都没有标点符号
string s3 = s1 + "," + s2 + '\n'; // 正确
string s4 = s1 + ","; // 正确,把一个string对象和一个字面值相加
string s5 = "hello" + ","; // 错误,两个运算对象都不是string
string s6 = s1 + "," + "world"; // 正确,每个加法运算符都有一个运算对象是string
string s7 = "hello" + "," + s2; // 错误,不能把字面值直接相加
3.2.3 处理 string 对象中的字符
cctype
头文件中定义了一组标准库函数处理字符串中的字符
函数 | 功能 |
---|---|
isalnum(c) |
当c是字母或者数字时为真 |
isalpha(c) |
当c是字母时为真 |
iscntrl(c) |
当c是控制字符时为真 |
isdigit(c) |
当c是数字时为真 |
isgraph(c) |
当c不是空格但可打印时为真 |
islower(c) |
当c是小写字母时为真 |
isprint(c) |
当c是可打印字符时为真 |
ispunct(c) |
当c是标点符号时为真 |
isspace(c) |
当c是空白时为真 |
isupper(c) |
当c是大写字母时为真 |
isxdigit(c) |
当c是十六进制数字时为真 |
tolower(c) |
如果c是大写字母,输出对应的小写字母,否则原样输出c |
toupper(c) |
如果c是小写字母,输出对应的大写字母,否则原样输出c |
处理每个字符?使用基于范围的 for 语句
范围 for (range for)语句,遍历给定序列中的每个元素并对序列中的每个值执行某种操作 。语法形式如下:
for(declaration:expression)
statement
expression
部分是一个对象,用于表示一个序列。declaration
部分负责定义一个变量,该变量被用于访问序列中的基础元素。每次迭代,declaration
部分的变量会被初始化为expression
部分的下一个元素值。
// 使用范围 for 语句和 ispunct 函数来统计 string 对象中标点符号的个数
string s("Hello World!!!");
decltype(s.size()) punct_cnt = 0;
for(auto c : s) // 对于 s 中的每个字符
if(ispunct(c)) // 如果字符是标点
++punct_cnt; // 标点符号的计数值加 1
cout << punct_cnt << endl;
使用范围 for 语句改变字符串中的字符
如果想改变字符串中字符的值,必须把循环变量定义为引用类型。
string s("Hello World!!!");
for(auto &c : s) // 对 s 中的每个字符
if(islower(c)) // 如果是小写
c = toupper(c); // 改为大写
cout << s << endl;
只处理一部分字符?
访问string
单个字符有两种方式:一种是使用下标运算符[]
,一种是使用迭代器。
下标运算符[]
接受的输入参数是string::size_type
类型的值,表示要访问的字符的位置;返回值是该位置上字符的引用。
string
对象的下标必须大于等于 0 而小于 s.size()
。
使用下标进行迭代
// 将字符串的第一个词改为大写
string s("hello World!!!");
for(decltype(s.size()) i = 0; i != s.size() && !isspace(s[i]); ++i)
{
s[i] = toupper(s[i]);
}
使用下标进行随机访问
// 把 0 到 15 之间的十进制数转换成对应的十六进制
const string hexdigits = "0123456789ABCDEF"; // 可能的十六进制数字
cout << "input numbers: " << endl;
string result; // 用于保存十六进制的字符串
string::size_type n; // 用于保存从输入流读取的数
while(cin >> n)
if(n < hexdigits.size()) // 忽略无效输入
result += hexdigits[n]; // 得到对应的十六进制数
cout << result << endl;
无论何时用到字符串的下标,都应该注意检查其合法性。