安全提取字符串中首个带符号整数应手动扫描:跳过非数字字符,识别首字符正负号,逐位累加并实时防溢出,避免std::stoi异常;regex虽简洁但性能较低且默认返回首个匹配。

提取字符串里的数字并转成整数
直接用 std::stoi 会崩溃,因为只要开头不是数字就抛 std::invalid_argument;而 std::stol 同理。真正安全的做法是先跳过非数字字符,再定位连续数字段——但注意:负号、小数点、科学计数法这些都不算“纯数值部分”,按题意默认只处理带符号的整数片段(如 "abc-123def" → -123)。
推荐用 std::regex 找第一个匹配的整数模式:R"([-+]?\d+)",它能捕获带正负号的连续数字:
std::string s = "item#42: price -789";
std::regex re(R"([-+]?\d+)");
std::smatch match;
if (std::regex_search(s, match, re)) {
int num = std::stoi(match.str());
// num == 42,不是 -789 —— 因为 42 出现在前面
}
如果要取最后一个数字,得用 std::regex_iterator 遍历所有匹配,或改用手动扫描:
- 从左到右扫描,记录每段数字起始位置和符号
- 遇到非数字字符(除首字符可能是
'-'或'+')就中断当前数字 - 用
std::strtol+const char*指针更可控,避免异常
减去数值部分时别忽略符号和边界
提取出的数要参与减法,常见错误是把字符串里多个数字当成一个整体处理,比如 "a1b2c3" 被误读成 123。实际应按“独立数值片段”理解——每个连续数字串单独解析。
减法本身没陷阱,但要注意:
-
std::stoi在溢出时抛异常,std::strtol返回LONG_MAX/LONG_MIN并设errno = ERANGE,需检查 - 若原字符串无数字,
std::regex_search失败,match.str()为空,std::stoi("")直接崩溃 - 减法结果类型要匹配:
int减long long可能截断,建议统一用long long存储中间值
手写扫描比正则更快且可控
正则在短字符串上开销不大,但嵌入式或高频调用场景下,手写线性扫描更稳。核心逻辑三步走:
long long extract_first_number(const std::string& s) {
size_t i = 0;
while (i = s.length()) return 0; // 没找到数字
<pre class="brush:php;toolbar:false;">int sign = 1;
if (s[i] == '-' || s[i] == '+') {
sign = (s[i] == '-') ? -1 : 1;
++i;
}
long long val = 0;
while (i LLONG_MAX / 10) break; // 防溢出
++i;
}
return sign * val;}
这个函数返回第一个完整整数,不依赖异常,也不吃掉后续字符。关键点:
- 先跳过所有非数字/非符号字符,不假设数字一定在开头
- 只认紧邻的符号(
-123合法,a-123也合法;但--123只取第一个-) - 手动进位计算,边算边检查溢出,比
std::stoll更透明
减法操作本身要明确目标
题目说“减去数值部分”,但没说明减谁。常见两种意图:
- 从原始字符串代表的某个基准值中减去提取的数(如配置项
"offset=+42",基准是 100,则结果是100 - 42) - 把字符串中所有数字部分依次减掉(如
"x1y2z3"→1 - 2 - 3 == -4)
前者只需一次提取;后者必须遍历全部数字片段。用 std::regex_iterator 更适合后者:
std::vector<long long> nums;
for (std::sregex_iterator it(s.begin(), s.end(), re); it != std::sregex_iterator(); ++it) {
nums.push_back(std::stoll(it->str()));
}
long long result = nums.empty() ? 0 : nums[0];
for (size_t i = 1; i <p>注意 <code>std::stoll</code> 对空匹配仍会异常,所以必须确保 <code>it->str()</code> 非空——而 <code>regex_search</code> 和 <code>sregex_iterator</code> 保证匹配成功才返回非空字符串。</p>
<p>最易被忽略的是:数字可能以 0 开头(如 <code>"007"</code>),<code>std::stoi</code> 会当八进制?不会——C++ 标准库的转换函数默认十进制,前导零不影响值,<code>std::stoi("007") == 7</code>。但如果你自己手写解析,别误把 <code>'0'</code> 当终止符。</p></long>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











