在 C++ 中,整数 123 和字符串 "123" 虽然打印到屏幕上看起来一模一样,但它们的底层类型与运算规则完全不同。整数做 + 运算是数学加法,而字符串做 + 运算则是把两段文本首尾拼在一起,比如下面这段代码:
int num = 123;
std::cout << num + 1 << '\n'; // 整数相加:输出 124
std::string str = "123";
std::cout << str + "1" << '\n'; // 字符串拼接:输出 1231由于数值与文本的存储格式完全不同,我们不能使用 static_cast 直接转换它们,而是借助 <string> 头文件中的 2 个标准函数:调用 std::to_string() 将整数转为字符串,调用 std::stoi() 将字符串解析为整数。
int 转 string:std::to_string()
在 C++ 中,想要把 int 整数转换为 std::string 字符串,最直接的方式就是调用 <string> 头文件中的 std::to_string() 函数。
语法:
std::to_string(整数)说明:
std::to_string() 会将传入的整数按十进制格式转换成一个新的 std::string 字符串并返回。除了 int 之外,它也直接支持 long、long long、unsigned int 等其他整数类型。
示例 1:整数转为 string
#include <iostream>
#include <string>
int main() {
int orderId = 2026;
std::string text = std::to_string(orderId);
std::cout << text << '\n';
std::cout << "订单编号:ID-" << text << '\n';
return 0;
}运行结果如下。
2026
订单编号:ID-2026分析:
在这个例子中,我们调用 std::to_string(orderId) 后,整数 2026 被转换成了字符串 "2026",随后就能拼在 "订单编号:ID-" 后面一起输出。
这里小伙伴们要注意,平时做字符串拼接时,C++ 不支持直接用 + 号把 std::string 和 int 整数相加。下面这种省掉转换步骤的写法就是错误的,编译阶段会直接报错。
int id = 2026;
std::string prefix = "ID-";
std::string wrongStr = prefix + id; // 报错:不能直接相加string 转 int:std::stoi()
反过来,如果我们手里拿到的是数字字符串,希望转换成真正的 int 整数参与计算,可以使用 <string> 头文件中的 std::stoi() 函数。其中,stoi 是英文 “string to integer(字符串转整数)” 的缩写。
1. 基本用法
在日常十进制转换中,我们直接把 std::string 字符串传给 std::stoi() 即可。
语法:
std::stoi(字符串)说明:
std::stoi() 会自动跳过字符串开头可能存在的空格,然后将后面的十进制数字文本解析为 int 整数并返回,正数和负数(如 "-45")都可以正常识别。
示例 2:字符串解析为 int
#include <iostream>
#include <string>
int main() {
std::string text = "128";
int count = std::stoi(text);
std::cout << count << '\n';
std::cout << count + 2 << '\n';
return 0;
}运行结果如下。
128
130分析:
std::stoi(text) 表示将字符串 "128" 解析为 int 整数 128 并赋值给变量 count。转换完成后,后面的 count + 2 执行的就是整数加法,得到结果 130。
除了转成 int 的 std::stoi() 之外,如果字符串里的整数非常大、超出了 int 的取值范围(约 ±21 亿),<string> 头文件中还提供了同一系列的其他转换函数,如下表所示。
| 函数名 | 返回类型 | 说明 |
|---|---|---|
| std::stoi() | int | 最常用,转换为普通有符号整型 |
| std::stol() | long | 转换为长整型 |
| std::stoll() | long long | 转换为超长整型,适合超过 21 亿的大整数 |
| std::stoul() | unsigned long | 转换为无符号长整型 |
2. 部分解析与停止位置
std::stoi() 有一个很实用的特点:它并不要求整串文本全部都是数字。只要字符串开头是合法的整数,当往后读到字母、小数点或其他符号时,它就会停下来,把前面已经读到的整数返回给我们。
比如调用 std::stoi("120px") 会返回整数 120,调用 std::stoi("3.14") 会在遇到小数点 '.' 时停下并返回整数 3。如果我们还想知道它到底读到了哪个下标位置才停下,可以传入第 2 个参数。
语法:
std::stoi(字符串, 停止位置指针)说明:
其中,第 2 个参数是 std::size_t* 类型的指针,用来记录停止解析的字符下标。std::size_t 是标准库中专门表示长度与下标的无符号整数类型。
因为函数返回值已经用来返回转出的整数了,所以我们先定义变量 pos,再传入地址 &pos 让函数把停止下标写进去。平时不关心停止位置时,直接省略第 2 个参数即可。
示例 3:获取停止解析的位置
#include <iostream>
#include <string>
int main() {
std::string width = "120px";
std::size_t pos = 0;
int num = std::stoi(width, &pos);
std::cout << num << '\n';
std::cout << pos << '\n';
std::cout << width.substr(pos) << '\n';
return 0;
}运行结果如下。
120
3
px分析:
字符串 "120px" 开头的数字 120 占了 3 个字符(下标 0、1、2),解析在字符 'p' 所在的下标 3 处停下,因此变量 pos 的值被更新为 3。接着我们调用前面学过的 substr() 函数从下标 3 开始截取,就能拿到剩余的单位 "px"。
如果业务场景要求 “整串文本必须完全是纯数字,不能带有任何多余后缀”,我们可以在转换后检查 pos == width.size() 是否成立,如下所示:
std::size_t pos = 0;
int num = std::stoi(width, &pos);
if (pos != width.size()) {
// 包含未解析的后缀字符
}3. 指定进制转换
std::stoi() 默认按照十进制来解析字符串,同时还支持通过第 3 个参数指定进制,方便我们把二进制或十六进制字符串直接转成十进制 int 整数。
语法:
std::stoi(字符串, 停止位置指针, 进制)说明:
第 3 个参数是一个整数,用来指定按多少进制解析,默认值为 10,常用的还有 2(二进制)、8(八进制)和 16(十六进制)。如果当前只想指定进制、不需要记录停止解析的位置,把第 2 个指针参数传为 nullptr 占位即可。
示例 4:解析不同进制字符串
#include <iostream>
#include <string>
int main() {
// 按二进制解析
int binVal = std::stoi("1010", nullptr, 2);
// 按十六进制解析
int hexVal = std::stoi("FF", nullptr, 16);
std::cout << binVal << '\n';
std::cout << hexVal << '\n';
return 0;
}运行结果如下。
10
255分析:
"1010" 按二进制解析后对应的十进制整数是 10,"FF"(或 "0xFF")按十六进制解析后对应的十进制整数是 255。参数中间的 nullptr 表示不记录停止解析的下标位置。
转换失败的异常处理
如果传给 std::stoi() 的字符串开头根本没有数字(比如 "hello"),函数会不会返回 0 呢?
实际上并不会。因为 0 本身就是一个完全合法的转换结果,比如 std::stoi("0") 返回的就是 0。如果转换失败也返回 0,程序就分不清到底是 “成功转出了整数 0” 还是 “转换失败了”。
因此,当 std::stoi() 转换失败时,会直接抛出 <stdexcept> 头文件中的以下 2 种标准异常:
- std::invalid_argument:当传入
"hello"或空字符串""等开头没有任何有效整数的文本时,抛出该异常。 - std::out_of_range:当字符串虽然是合法整数,但数值超出了
int类型能表示的范围时,抛出该异常。
示例 5:捕获转换失败的异常
#include <iostream>
#include <stdexcept> // 提供标准异常类
#include <string>
void parseNumber(const std::string& text) {
try {
int num = std::stoi(text);
std::cout << num << '\n';
} catch (const std::invalid_argument&) {
std::cout << text << ":不是有效整数" << '\n';
} catch (const std::out_of_range&) {
std::cout << text << ":超出 int 范围" << '\n';
}
}
int main() {
parseNumber("hello");
parseNumber("99999999999999999999");
return 0;
}运行结果如下。
hello:不是有效整数
99999999999999999999:超出 int 范围分析:
为了方便连续测试两组非法文本,这里我们定义了一个辅助函数 parseNumber(),并在函数内部使用 try...catch 捕获 std::stoi() 抛出的异常。
当我们第 1 次调用并传入 "hello" 时,由于首字符就无法解析出整数,程序触发了 std::invalid_argument 异常。当我们第 2 次传入超长数字字符串时,数值超出了 int 的取值上限,于是触发了 std::out_of_range 异常。
在实际开发中,只要待转换的字符串来自用户输入或外部文件,我们都建议像这样加上异常捕获,避免程序因为输入格式有误而直接异常终止。
常见问题
1. C++ 中还能使用 C 语言的 atoi() 函数把字符串转成整数吗?
可以使用,但在现代 C++ 开发中并不推荐,小伙伴们平时优先使用 std::stoi() 即可,主要有以下 2 个原因:
参数传递更方便:std::stoi() 可以直接接收 std::string 对象,而 <cstdlib> 中的 atoi() 只接收 const char* 指针,每次调用还要多写一次 .c_str()。
错误更容易排查:传入 "abc" 这类非法文本时,atoi() 不会报错,而是静默返回 0,导致无法区分 "0" 和转换失败。而 std::stoi() 会明确抛出异常,方便大家及时定位问题。
2. std::to_string() 可以把整数转换成十六进制或二进制字符串吗?
不能。std::to_string() 固定只按十进制格式生成字符串,没有提供指定进制的参数。如果需要将整数格式化为十六进制或二进制字符串,我们可以使用后面将要学习的 std::format() 函数。
