如何编写容错性更强的C++用户输入代码?排查现有错误处理
代码错误处理问题排查
原代码存在以下几处关键问题,会导致坏输入引发程序崩溃或逻辑异常:
- 初始输入
noOfInput无校验:若用户输入非整数(如字符串、小数),std::cin >> noOfInput会失败,noOfInput值未定义,后续循环会基于无效值执行,可能引发无限循环或非法内存访问。 - 输入错误时循环计数器未回退:某一行输入无效时,程序仅提示重试但
i仍会自增,导致本该重新输入的条目被跳过,最终收集的条目数量不足。 - 字符串查找结果未做有效性检查:
find_first_not_of、find等函数可能返回std::string::npos(未找到目标),原代码直接用该结果执行substr,会触发未定义行为(程序崩溃)。 - 异常捕获范围不全:
std::stoi和std::stod除了抛出std::logic_error子类,还会抛出std::out_of_range(数值超出类型范围),原代码未捕获该异常,会导致程序崩溃。 - 错误输入后未重新获取输入:清理
cin错误状态后直接进入下一次循环,未让用户重新输入当前条目的内容。 - 未限制
noOfInput的合理范围:若用户输入负数或极大值,会导致循环逻辑异常(负数时循环不执行,极大值时可能耗尽内存)。
代码优化方案
针对上述问题,从输入校验、错误恢复、边界检查等方面优化代码,提升容错性:
核心优化点
- 校验初始输入的有效性,确保
noOfInput是1到合理最大值之间的整数。 - 输入错误时回退循环计数器,让程序重新处理当前条目。
- 检查所有字符串查找结果,若返回
npos则直接触发错误处理。 - 扩大异常捕获范围,覆盖
stoi/stod的所有异常类型。 - 错误后重新获取输入,确保用户有机会修正输入。
- 限制最大条目数,避免恶意输入耗尽系统资源。
优化后的代码
#include <iostream> #include <string> #include <vector> #include <limits> #include <stdexcept> // 补充原代码缺失的itemInfo定义 struct itemInfo { int quantity; std::string name; double actualPrice; }; int main() { int noOfInput; const int MAX_ITEMS = 1000; // 限制最大条目数,避免恶意输入 // 循环获取有效的产品数量 while (true) { std::cout << "请输入产品数量(1-" << MAX_ITEMS << "):"; if (std::cin >> noOfInput && noOfInput > 0 && noOfInput <= MAX_ITEMS) { break; } else { std::cin.clear(); std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); std::cout << "输入无效,请输入1到" << MAX_ITEMS << "之间的整数。\n"; } } std::string line; std::vector<itemInfo> itemsVec; itemsVec.reserve(noOfInput); // 预分配内存,提升性能 // 循环获取产品信息,仅在输入有效时递增计数器 for (auto i = 0; i < noOfInput; ) { std::cout << "请输入第" << (i+1) << "个产品(格式:数量 名称 at 价格):"; std::getline(std::cin >> std::ws, line); // 检查输入读取是否失败 if (!std::cin) { std::cin.clear(); std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); std::cout << "输入读取失败,请重新输入。\n"; continue; } try { itemInfo item; size_t start, stop; // 解析数量:检查空白处理和有效性 start = line.find_first_not_of(" \t"); if (start == std::string::npos) { throw std::invalid_argument("输入为空,缺少数量信息"); } stop = line.find_first_of(" \t", start); if (stop == std::string::npos) { throw std::invalid_argument("缺少产品名称和价格信息"); } item.quantity = std::stoi(line.substr(start, stop - start)); if (item.quantity <= 0) { throw std::invalid_argument("数量必须是正整数"); } // 解析名称:检查分隔符和空值 start = line.find_first_not_of(" \t", stop + 1); if (start == std::string::npos) { throw std::invalid_argument("缺少产品名称"); } stop = line.find(" at ", start); if (stop == std::string::npos) { throw std::invalid_argument("格式错误,未找到\" at \"分隔符"); } item.name = line.substr(start, stop - start); if (item.name.empty()) { throw std::invalid_argument("产品名称不能为空"); } // 解析价格:检查有效性和范围 start = line.find_first_not_of(" \t", stop + 4); if (start == std::string::npos) { throw std::invalid_argument("缺少价格信息"); } item.actualPrice = std::stod(line.substr(start)); if (item.actualPrice <= 0) { throw std::invalid_argument("价格必须大于0"); } // 所有校验通过,添加到容器并递增计数器 itemsVec.push_back(item); i++; } catch (const std::exception& ex) { std::cout << "输入错误:" << ex.what() << ",请重新输入。\n"; } } // 可选:输出已收集的产品信息用于验证 std::cout << "\n已成功输入" << itemsVec.size() << "个产品:\n"; for (const auto& item : itemsVec) { std::cout << item.quantity << " " << item.name << " at " << item.actualPrice << "\n"; } return 0; }
内容的提问来源于stack exchange,提问作者Falcon
相关产品推荐
相关产品推荐

