标准库类型string
表示可变长的字符序列,使用string
类型必须首先包含头文件#include <string>
。作为标准库的一部分,string
定义在命名空间std
中。
1 定义和初始化string
对象
下表列出了初始化string
对象的方式。
表 1:初始化string | ||
---|---|---|
语句 | 描述 | |
1 | string s1 | 默认初始化,s1 是一个空字符串。 |
2 | string s2(s1) | s2 是s1 的副本。 |
3 | string s2 = s1 | 等价于s2(s1) ,s2 是s1 的副本。 |
4 | string s3("value") | s3 是字面值"value" 的副本,除了字面值最后的那个空字符外。 |
5 | string s3 = "value" | 等价于s3("value") ,s3 是字面值"value" 的副本。 |
6 | string s4(n, 'c') | 把s4 初始化为由连续n 个字符c 组成的串。 |
2 读写string
对象
2.1 输入输出流
输入输出流也可以读写string
对象,例如:
string s; // 默认初始化空字符串
std::cin >> s; // 将 string 对象读入 s,遇到空白停止
std::cout << s << std::endl; // 输出 s
在执行读取操作时,string
对象会自动忽略开头的空白(即空格符、换行符、制表符等),并从第一个真正的字符开始读起,直到遇到下一处空白为止。因此,如果程序的输入是" Hello "
,则输出将是"Hello"
,输出结果中没有任何空格。
此外,也可以使用while
语句实现读取未知数量的字符串对象,例如:
string word;
while (std::cin >> word) { // 反复读取,直至到达文件末尾std::cout << word << std::endl; // 逐个输出单词,每个单词后面紧跟一个换行
}
2.2 使用getline()
读取一整行
有时我们希望能在最终得到的字符串中保留输入时的空白符,这时应该用getline()
函数代替原来的>>
运算符。getline()
函数的参数是一个输入流和一个string
对象,函数从给定的输入流中读入内容,直到遇到换行符为止(注意换行符也被读进来了),然后把所读的内容存入到那个string
对象中去(注意不存在换行符)。getline()
只要一遇到换行符就结束读取操作并返回结果,哪怕输入的一开始就是换行符。如果输入真的一开始就是换行符,那么所得的结果是个空string
。
和输入运算符一样,getline()
也会返回它的流参数。因此既然输入运算符能作为判断的条件,我们也能用getline()
的结果作为条件。例如,我们要读取不定数量的行:
string line;
while (getline(std::cin, line)) { // 每次读入一整行,直至到达文件末尾std::cout << line << std::endl;
}
因为line
中不包含换行符,所以我们手动地加上换行操作符。
3 字符串的运算
3.1 索引
s[n]
返回
s
中第n
个字符的引用,位置n
从 0 计起。
3.2 拼接
两个string
对象相加(+
)得到一个新的string
对象,其内容是把左侧的运算对象和右侧的运算对象串接而成。复合赋值运算符(+=
)负责把右侧string
对象的内容追加到左侧string
对象的后面。例如:
string s1 = "Hello, ", s2 = "World";
string s3 = s1 + s2; // s3 的内容是 "Hello World"
s1 += s2; // 等价于 s1 = s1 + s2
即使一种类型并非所需,我们也可以使用它,前提是该种类型可以自动转换成所需的类型。因为标准库允许把字符字面值和字符串字面值转换成string
对象,所以在需要string
对象的地方就可以使用这两种字面值来替代。当把string
对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是string
对象。例如:
string s4 = s1 + ", "; // 正确:把一个 string 对象和一个字面值相加
string s5 = "Hello" + ", "; // 错误:两个运算对象都不是 string
string s6 = s1 + ", " + "World"; // 正确:每个加法运算符都有一个运算对象是 string
string s7 = "Hello" + ", " + s2; // 错误:不能把字面值直接相加
警告:因为某些历史原因,也为了与 C 兼容,所以 C++ 中的字符串字面值并不是标准库类型string
的对象。切记,字符串字面值与string
是不同的类型。
3.3 比较
s1 == s2
、s1 != s2
如果
s1
和s2
中所含的字符完全一样,则它们相等。
<
、<=
、>
、>=
利用字符在字典中的顺序进行比较。
4 常用方法
4.1 长度
s.size()
返回字符串
s
中字符的个数,即对象的长度。
s.empty()
s
为空返回true
,否则返回false
。
4.2 子字符串操作
s.substr(pos, n)
返回原始
string
对象的一部分或全部的拷贝。传递给substr()
的是可选的开始位置和计数值。例如:string s("Hello World"); string s2 = s.substr(0, 5); // s2 = Hello string s3 = s.substr(6); // s3 = World string s4 = s.substr(6, 11); // s4 = World string s5 = s.substr(12); // 抛出一个 out_of_range 异常
如果开始位置超过了
string
的大小,则会抛出一个out_of_range
异常。如果开始位置加上计数值大于string
的大小,则会调整计数值,只拷贝到string
的末尾。
4.3 处理string
对象中的字符
我们经常需要单独处理string
对象中的字符。这类处理的一个关键问题是如何获取字符本身。另一个关键问题是要知道能改变某个字符的特性。在cctype
头文件中定义了一组标准库函数来处理这部分工作,下表列出了主要的函数名及其含义。
表 2:cctype 头文件中的函数。 | ||
---|---|---|
语句 | 描述 | |
1 | isalpha(c) | c 是否为字母。 |
2 | isdigit(c) | c 是否为数字。 |
3 | isalnum(c) | c 是否为字母或数字。 |
4 | islower(c) | c 是否为小写字母。 |
5 | isupper(c) | c 是否为大写字母。 |
6 | isxdigit(c) | c 是否为十六进制数字。 |
7 | iscntrl(c) | c 是否为控制字符。 |
8 | isgraph(c) | c 是否为不是空格但可打印。 |
9 | isprint(c) | c 是否为可打印字符(即空格或具有可视形式)。 |
10 | ispunct(c) | c 是否为标点符号(即不是控制字符、数字、字母、可打印空白中的一种)。 |
11 | isspace(c) | c 是否为空白(即空格、横向制表符、纵向制表符、回车符、换行符、进纸符)。 |
12 | tolower(c) | 如果c 是大写字母,输出对应的小写字母;否则原样输出c 。 |
13 | toupper(c) | 如果c 是小写字母,输出对应的大写字母;否则原样输出c 。 |
4.3.1 处理每个字符
如果想对string
对象中的每个字符做点操作,目前最好的办法是使用范围for
语句。一个string
对象表示一个字符的序列,因此string
对象可以作为范围for
语句中的expression
部分。
如果想要改变string
对象中字符的值,必须把循环变量定义成引用类型。使用引用作为循环控制变量时,这个变量实际上被依次绑定到了序列的每个元素上。使用这个引用,我们就能改变它绑定的字符。例如,假设我们想把一个字符串改写为大写字母的形式:
std::string s("Hello World!!!");
for (auto &c : s)c = toupper(c); // c 是一个引用,因此赋值语句将改变 s 中字符的值
4.3.2 只处理一部分字符
要想访问string
对象中的单个字符有两种方式:一种是使用下标,另外一种是使用迭代器。
下标运算符[]
接收的输入参数是string::size_type
类型的值,这个参数表示要访问的字符的位置;返回值是该位置上字符的引用。
string
对象的下标从 0 计起。下标的值称为“下标”或“索引”,任何表达式只要它的值是一个整型值就能作为索引。不过,如果某个索引是带符号类型的值,将自动转换成由string::size_type
表达的无符号类型。如果使用下标访问空string
将会引发不可预知的结果。
只要字符串不是常量,就能为下标运算符返回的字符赋新值。