跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

C++ 怎么从字符串提取数字 C++ stoi与stod异常处理教程【健壮性】

stoi/stod 不加防护必出错,因它们严格校验格式且不返回解析位置;安全解析需用 strtol/strtod 获取结束指针,或手动扫描提取数字子串并捕获异常。 直接用
stoi
或
stod
解析字符串里的数字,只要输入含非数字字符(比如
"123abc"
、
" 45.6"
、空串、纯空格),程序就崩溃或行为未定义——这不是“能不能用”的问题,是“不加防护就一定出事”。 stoi/stod 默认只认前导空白 + 符号 + 数字,其余全算错误 这两个函数底层依赖
std::strtol
/
std::strtod
,规则很严格:
"123"
→ 成功;
"123abc"
→ 抛
std::invalid_argument
(不是截取 123)
" 45.6"
→ 成功(自动跳过前导空格);
"\t\n45.6"
→ 同样成功
" "
或
""
→ 抛
std::invalid_argument
"1e1000"
(溢出)→ 抛
std::out_of_range
它们**不提供“解析到哪为止”的位置信息,也不容忍尾部垃圾字符**。想安全提取,必须自己控制解析边界。 用 strtol/strtod 替代,拿到解析结束位置再判断
strtol
和
strtod
是 C 标准库 函数,但 C++ 中仍可直接用,优势是返回
char*
指向第一个未转换字符,你能据此判断是否“整段合法”或“只取前面有效部分”: 立即学习 “ C++免费学习笔记(深入) ”; C函数速查手册(CHM版) C函数速查手册(CHM版) 下载
std::string s = "123abc"; char* end; long val = std::strtol(s.c_str(), &end, 10); if (end == s.c_str()) { // 一个数字都没识别到(如空串、纯字母) } else if (*end == '\0') { // 全部字符都参与了转换 → 完全合法 } else { // end 指向 'a',说明 "123" 是有效前缀,后面是垃圾 }
注意:
strtol
不抛异常,错误时返回 0 或
LONG_MIN/LONG_MAX
并设置
errno
;
strtod
同理,但需检查
errno == ERANGE
判断溢出。 提取“字符串中第一个连续数字子串”要手动扫描 如果原始需求是像从
"price: $123.45 (tax included)"
中捞出
123.45
,
stoi/stod
完全无能为力——它们要求整个字符串符合格式。此时得自己遍历: 跳过开头非数字、非符号、非小数点的字符 记录起始位置,继续读直到遇到非法字符(如字母、多于一个小数点、连着两个符号) 对截出的子串调用
stod
,并用
try/catch
捕获溢出 示例关键逻辑:
size_t i = 0; // 跳过非起始字符 while (i < s.length() && !std::isdigit(s[i]) && s[i] != '-' && s[i] != '+') ++i; size_t start = i; // 扫描数字主体(允许一个 '.',不允许 "1.2.3") int dot_count = 0; while (i < s.length()) { if (std::isdigit(s[i])) { ++i; } else if (s[i] == '.' && dot_count == 0) { ++dot_count; ++i; } else { break; } } if (i > start) { std::string num_str = s.substr(start, i - start); try { double d = std::stod(num_str); // 使用 d } catch (const std::exception&) { // 溢出或格式异常(如 ".e") } }
别忽略 locale 和负号嵌套等边界情况 默认 C locale 下
strtol
认
"-123"
,但若程序切换过 locale(比如用了
std::locale::global(std::locale("zh_CN"))
),某些 locale 可能允许千分位符号或不同负号,导致解析错位。更隐蔽的是:
"--123"
、
"-+45"
这类输入,
strtol
会停在第一个
'-'
后,认为无效,但你可能期望跳过所有前导符号——这只能靠预处理。健壮性真正的难点不在“怎么转”,而在“怎么定义什么是‘可接受的输入’”。

相关文章