解析文件Token存入vector后访问抛出std::out_of_range错误求助
核心错误:switch语句缺少break导致Token分类完全失效
你的代码里有两处致命的switch逻辑错误,直接导致Token解析和排序完全偏离预期,最终触发std::out_of_range异常:
1. Token::parse函数的switch无break
解析Token类型时,无论匹配到哪个case,代码都会继续执行后续所有case的赋值操作,最终所有Token的level都会被覆盖为最后一个case的值(FOUR_CHILD)。比如:
- 输入行
@use 1;的lev为'1',代码先执行case '1'设置tpe=ONE_CHILD,但因为没有break,会继续执行case '2'、case '3'、case '4',最终tpe被强制改为FOUR_CHILD。 - 所有Token的
level最终都变成5,排序时全部被归入st.fourth和st.unspec,st.first、st.second等容器完全为空——这就是调用st.first.at(0)抛出异常的根本原因。
修复代码:给每个case添加break,同时初始化tpe避免未定义行为:
static Token parse(string line) { string temp; istringstream iss(line); char lev; u8 tpe = Token::UNSPEC; // 初始化默认值 iss >> temp >> lev; switch (lev) { case 'c': tpe = Token::UNSPEC; break; case '0': tpe = Token::NO_CHILD; break; case '1': tpe = Token::ONE_CHILD; break; case '2': tpe = Token::TWO_CHILD; break; case '3': tpe = Token::THREE_CHILD; break; case '4': tpe = Token::FOUR_CHILD; break; default: cerr << "警告:无效的Token级别 '" << lev << "',行内容:" << line << endl; break; } return Token(tpe, temp); }
2. TokenScraper::sort函数的switch无break
排序时的switch同样缺少break,会导致一个Token被同时加入多个分类容器(比如NO_CHILD类型的Token会被加入none、first、second等所有后续容器),不符合分类需求。
修复代码:给每个case添加break,同时用size_t代替int作为循环变量(匹配vector::size()的无符号类型):
struct SortedTokens TokenScraper::sort() { SortedTokens st; for (size_t i = 0; i < tokens.size(); i++) { switch (tokens.at(i).level) { case Token::NO_CHILD: st.none.push_back(tokens.at(i)); break; case Token::ONE_CHILD: st.first.push_back(tokens.at(i)); break; case Token::TWO_CHILD: st.second.push_back(tokens.at(i)); break; case Token::THREE_CHILD: st.third.push_back(tokens.at(i)); break; case Token::FOUR_CHILD: st.fourth.push_back(tokens.at(i)); break; case Token::UNSPEC: st.unspec.push_back(tokens.at(i)); break; default: break; } } return st; }
其他次要问题修复
1. IOError构造函数逻辑错误
当前构造函数中,message.append(to_string(line))操作的是参数副本,而非类成员变量,导致错误信息拼接失败。修改为:
struct IOError { string message; IOError(string msg, int line) : message(msg + to_string(line)) {} };
2. 异常抛出方式错误
构造函数中抛出new IOError是抛出指针,C++通常抛出异常对象而非指针,避免内存泄漏:
TokenScraper::TokenScraper(string filename) { file.open(filename); if (!file.is_open() || file.peek() == file.eof()) { throw IOError("无法打开文件,行号:", __LINE__); } }
throw之后的代码不会执行,因此可以删除file.close()和return。
验证效果
修复上述问题后,st.first会包含所有子节点数为1的Token(@use、@start、@include、import),st.second会包含子节点数为2的Token(@eval、@def、@alias),此时访问st.first.at(0)和st.second.at(0)将不再抛出std::out_of_range异常。
内容的提问来源于stack exchange,提问作者JACK SUMMERS

