在 C++ 中,基础的 char 类型只能用来保存单个字符,比如 'A' 或 '8'。但在实际开发中,我们经常需要处理姓名、网址、提示语等一整串字符,也就是 “字符串”。
处理字符串最常用的是 C++ 标准库提供的 std::string 类型。你可以把它看成一个能自动伸缩的字符容器:不用提前计算文本有多长,它会自动分配和调整存储空间,用起来非常省心。
C++ string 的定义
与 int、double、char 这些内置的数据类型不同,std::string 是由 C++ 标准库提供的。因此在使用它之前,我们需要先在代码开头引入 <string> 头文件。
1. 基本定义与初始化
在 C++ 中,定义一个 std::string 变量并赋予初始文本,语法格式如下。
语法:
#include <string>
std::string 变量名 = "字符串内容";说明:
std::string 是完整的类型名称,前面的 std:: 表示它位于标准库的命名空间 std 中,双引号里的内容就是我们要保存的字符串文本。
初学的小伙伴很容易把 char 和 std::string 的引号搞混。其实区分起来很简单:char 表示单个字符,必须使用英文单引号(');std::string 表示一串字符,必须使用英文双引号(")。
char ch = 'L'; // 单个字符用单引号
std::string site = "lvye"; // 字符串用双引号示例 1:定义并输出字符串
#include <iostream>
#include <string>
int main() {
std::string message = "Hello, C++!";
std::cout << message << '\n';
return 0;
}运行结果如下。
Hello, C++!分析:
std::string message = "Hello, C++!"; 表示定义了一个名为 message 的字符串变量,并把双引号里的文本保存进去。接着通过 std::cout << message 就能把整段文字打印到屏幕上。
同样地,如果想要从键盘输入一个不带空格的单词,也可以配合 std::cin 写成 std::cin >> message;。至于如何读取包含空格的一整行文本,后面在 “C++ getline 读取字符串” 一节中会展开介绍。
2. 重新赋值与空字符串
定义好 std::string 变量之后,它的值跟普通变量一样是可以随时改变的。后面只需要使用等号(=)赋上一段新文本,就能直接覆盖掉原来的内容。哪怕新文本比原来长很多,std::string 也会自动扩容。
示例 2:修改字符串的值
#include <iostream>
#include <string>
int main() {
std::string course = "C++";
course = "Modern C++";
std::cout << course << '\n';
return 0;
}运行结果如下。
Modern C++分析:
course 一开始保存的是 "C++",执行 course = "Modern C++"; 后,新的文本直接覆盖了原来的内容,因此屏幕上输出了更新后的课程名称。
对于 std::string 变量的声明与赋值,下面 2 种方式是等价的:
// 方式 1
std::string site;
site = "lvyenet";
// 方式 2(推荐)
std::string site = "lvyenet";其中,方式 1 是先声明变量再赋值,而方式 2 是在声明的同时直接完成初始化。如果一开始就已经确定了要保存的文本内容,在实际开发中我们更推荐使用方式 2。
可能有些小伙伴会问:如果声明 std::string 变量时没有赋初值,它里面存的是什么呢?
跟未初始化会带有随机值的 int 局部变量不同,std::string 默认会自动初始化为一个 “空字符串”,也就是里面没有任何字符,等价于 ""。需要注意的是,空字符串 "" 和包含空格的字符串 " " 并不是一回事,下面我们通过一个例子来对比一下。
示例 3:区分空字符串与空格
#include <iostream>
#include <string>
int main() {
std::string emptyStr;
std::string spaceStr = " ";
std::cout << "[" << emptyStr << "]" << '\n';
std::cout << "[" << spaceStr << "]" << '\n';
return 0;
}运行结果如下。
[]
[ ]分析:
为了看清两者的区别,我们在输出 emptyStr 和 spaceStr 的两侧加上了中括号。
emptyStr 声明时没有赋初值,里面没有任何字符,输出结果为 []。而 spaceStr 的双引号中间敲了一个空格,它实际包含一个空格字符,输出结果为 [ ]。
访问与修改字符
std::string 内部保存的是一串按顺序排列的 char 字符。跟前面学过的数组一样,字符串里的每一个字符都有对应的下标,并且下标也是从 0 开始计算的。
以字符串 std::string word = "lvye"; 为例,各个字符与下标的对应关系如下表所示。
| 字符位置 | 下标 | 对应字符 | 访问写法 |
|---|---|---|---|
| 第 1 个字符 | 0 | 'l' | word[0] |
| 第 2 个字符 | 1 | 'v' | word[1] |
| 第 3 个字符 | 2 | 'y' | word[2] |
| 第 4 个字符 | 3 | 'e' | word[3] |
1. 使用下标 [] 访问字符
在 C++ 中,如果我们想获取字符串中某一个位置的字符,最常用的方式是在字符串变量名后面加上中括号 [],并在里面写上下标。
语法:
字符串变量[下标]说明:
如果字符串的长度为 n,那么有效下标的范围就是 0 ~ n - 1。其中,下标 0 对应第 1 个字符,下标 1 对应第 2 个字符,以此类推。
示例 4:通过下标 [] 访问字符
#include <iostream>
#include <string>
int main() {
std::string word = "lvye";
std::cout << word[0] << '\n';
std::cout << word[1] << '\n';
std::cout << word[3] << '\n';
return 0;
}运行结果如下。
l
v
e分析:
word[0] 表示获取第 1 个字符 'l',word[1] 表示获取第 2 个字符 'v',word[3] 则对应第 4 个字符 'e'。
注意 使用中括号 [] 访问字符时,C++ 不会检查下标范围。比如 "lvye" 的有效下标是 0 ~ 3,访问 word[4] 会发生下标越界,程序可能读出乱码甚至直接崩溃。
2. 使用 at() 函数访问字符
除了使用中括号 [] 之外,我们还可以调用 std::string 自带的 at() 函数来获取指定下标的字符。
std::string 内部提供了很多现成的工具函数,在 C++ 中也叫 “成员函数”。想要调用这些函数,只需要在字符串变量名后面加上英文点号(.),再写上函数名即可,就相当于 “让这个字符串执行某项操作”。
语法:
字符串变量.at(下标)说明:
at() 函数与中括号 [] 的作用相同,区别在于 at() 会在程序运行时自动检查下标范围。如果传入的下标超出了有效范围,程序会立即报出 std::out_of_range 越界错误并停止运行,方便我们及时排查问题。
示例 5:使用 at() 访问字符
#include <iostream>
#include <string>
int main() {
std::string text = "Hello";
std::cout << text.at(0) << '\n';
std::cout << text.at(4) << '\n';
return 0;
}运行结果如下。
H
o分析:
在这个例子中,text.at(0) 表示获取下标为 0 的首字符 'H',text.at(4) 表示获取下标为 4 的末尾字符 'o'。
在实际开发中,如果下标范围是已知的,或者已经在循环中控制好了边界,直接使用 text[i] 最简洁。如果下标来自外部输入、有可能超出有效范围,则可以使用 text.at(i)。
3. 修改单个字符
通过下标 [] 或 at() 定位到具体位置后,我们直接使用等号(=)赋上一个新的 char 字符,就可以修改原字符串中对应位置的字符。
语法:
字符串变量[下标] = '新字符';说明:
等号左边通过下标指定要修改的位置,等号右边则是替换进去的新字符。
示例 6:修改单个字符
#include <iostream>
#include <string>
int main() {
std::string word = "lvye";
word[0] = 'L';
std::cout << word << '\n';
return 0;
}运行结果如下。
Lvye分析:
word[0] 原本是小写字母 'l',执行 word[0] = 'L'; 后,首字符被修改为大写字母 'L',整个字符串就变成了 "Lvye"。
这里小伙伴们要注意,word[0] 代表的是字符串里的单个 char 字符,因此等号右边必须使用英文单引号(')。下面这种写法就是错误的,因为 "L" 使用双引号括起来时属于字符串,不能直接赋给单个 char 位置:
std::string word = "lvye";
word[0] = "L";C++ 遍历字符串
当我们需要从头到尾依次取出字符串里的每一个字符时,最方便的做法是直接使用上一章学过的范围 for 循环。
语法:
for (char 变量名 : 字符串变量) {
// 依次处理每一个字符
}说明:
每执行一次循环,程序都会自动从字符串中按顺序取出一个字符赋给循环变量,直到把整个字符串里的所有字符都取完为止。
示例 7:遍历字符串中的字符
#include <iostream>
#include <string>
int main() {
std::string text = "C++20";
for (char ch : text) {
std::cout << ch << ' ';
}
std::cout << '\n';
return 0;
}运行结果如下。
C + + 2 0分析:
在上面例子中,for (char ch : text) 会按从左往右的顺序,依次把 "C++20" 里的 5 个字符取出来赋给变量 ch,再通过 std::cout 打印出来。这种方式不需要我们手动计算字符串长度,也不用操心下标越界的问题。
这里要留意一点,在 for (char ch : text) 中,ch 只是每次复制出来的字符副本,修改 ch 并不会改变原字符串 text。如果需要结合下标修改原字符串里的字符,可以配合后面学习的 size() 函数与普通下标循环来实现。
