您现在的位置: 365建站网 > 365文章 > C++ string 转 int 四种方法对比:stoi、strtol、from_chars 与坑

C++ string 转 int 四种方法对比:stoi、strtol、from_chars 与坑

文章来源:365jz.com 点击数:23 更新时间:2024-04-27 07:01

C++ 里“字符串转整数”看着一行代码,实际要处理溢出、非法字符、前导空格、正负号与进制。标准做法有四条路:std::stoi 系列、istringstream、strtol 系、C++17 的 from_chars。本文对比它们的错误行为并给出选型建议。

C++ string转int方法与实例

一、std::stoi / stoll / stoul:最简洁

#include <string>
std::string s = "12345";
int n = std::stoi(s);            // 12345

size_t pos = 0;
int v = std::stoi(“42abc”, &pos); // v=42, pos=2(停在第一个非法字符)
int hex = std::stoi(“1F”, nullptr, 16); // 31,指定进制

特点:签名 stoi(const string&, size_t* idx = nullptr, int base = 10)。失败时抛异常:invalid_argument(没有可转换的字符)与 out_of_range(超出目标类型范围)。所以生产代码必须包 try/catch,否则脏数据会让程序崩溃。

int toIntOr(const std::string& s, int dft = 0) {
  try {
    size_t pos = 0;
    int v = std::stoi(s, &pos);
    // 检查尾部是否还有非空白字符
    while (pos < s.size() && std::isspace(static_cast<unsigned char>(s[pos]))) ++pos;
    if (pos != s.size()) return dft;      // 形如 "12ab" 视为非法
    return v;
  } catch (const std::exception&) {
    return dft;
  }
}

这是最常见的坑:stoi("12abc") 会安静地返回 12,不报错。校验尾部残留字符是必要步骤。

二、istringstream:适合一行多字段

#include <sstream>
std::istringstream is(s);
int n;
if (is >> n) { /* 成功 */ }
if (!is.eof()) { /* 后面还有多余内容 */ }

优点是能连续读多个值、自动跳过空白;缺点是失败时静默(n 未初始化或为 0)且不区分溢出与格式错,性能也不如 stoi。适合配置文件解析这类宽松场景。

三、strtol 系:C 风格但控制力最强

#include <cstdlib>
errno = 0;
char* end = nullptr;
long v = std::strtol(s.c_str(), &end, 10);
if (end == s.c_str())        { /* 完全无法解析 */ }
if (*end != '\0')            { /* 尾部有非法字符 */ }
if (errno == ERANGE)         { /* 溢出 */ }
if (v < INT_MIN || v > INT_MAX) { /* 超出 int 范围 */ }

strtol 能同时告诉你“解析到哪里停了”和“是否溢出”,是四者中信息最全的。注意它返回 long,转 int 还要自己判范围;调用前记得清 errno。

四、std::from_chars(C++17):最快、零开销

#include <charconv>
int n = 0;
auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), n);
if (ec == std::errc{} && ptr == s.data() + s.size()) {
  // 完整且合法
} else if (ec == std::errc::result_out_of_range) {
  // 溢出
} else if (ec == std::errc::invalid_argument) {
  // 非数字
}
// ptr != 末尾指针 表示尾部有多余字符

优势:不分配内存、不抛异常、不读 locale、不跳过前导空白、性能最高(适合高频解析如 JSON/协议解析)。限制也很明确:不接受前导空格与 + 号(会直接返回 invalid_argument),不支持浮点在部分实现(C++17 只整型,浮点 from_chars 到 MSVC/较新 GCC 才可用),不支持指定进制以外的写法。

五、四者对比与选型

  • 要求快速写完、逻辑简单 → stoi + try/catch + 尾部校验。
  • 性能敏感、大批量解析 → from_chars。
  • 需要精确区分“格式错/尾部残留/溢出” → strtol。
  • 一行里读多个值、格式宽松 → istringstream。

六、常见坑清单

  1. 负号与范围:-2147483648 字面量常被当作“先取正的 2147483648 再取负”导致溢出,字符串解析(stoi)则正确返回 INT_MIN;写测试时不要用表达式模拟。
  2. 空字符串:stoi/strtol 都会失败,from_chars 返回 invalid_argument,必须先判 empty()。
  3. 溢出到无符号:`stoul("-1")` 不报错,会 wrap 成极大值,这是很隐蔽的 bug;解析无符号时也要自己判负号。
  4. locale 千位分隔符:stoi/istringstream 依赖 locale,可能把 "1,000" 只读成 1。
  5. 前导零=八进制:strtol(s, &end, 0) 第三参传 0 会按“0x 十六进制、0 八进制”自动判进制,"012" 解析成 10 而不是 12。

七、常见问题 FAQ

问:能不能用 atoi?
答:不建议。atoi 无法区分 0 与解析失败,也不报溢出,是历史遗留接口。C 代码可用 strtol 代替。

问:反向转换(int 转 string)呢?
答:C++11 起 std::to_string(n);高频场景用 std::format(C++20)或 fmt 库,性能更好。

一句话建议:日常代码用 stoi+异常+尾校验;追求性能与严谨用 from_chars;需要错误细节用 strtol;忘掉 atoi。

生产内容,同时打通搜索引擎(SEO)与AI搜索引擎(GEO)流量入口的软件! 点击进入365建站器 »