C++使用std::ifstream读取.obj文件时陷入无限循环问题排查
Hey there! Let's break down the two frustrating issues you're facing with your .obj reader for the ray tracer—getting stuck in the while loop after the last line, and that unhandled std::ios_base::failure exception. I'll walk through common causes and fixes for both.
1. Stuck While Loop After Reading the Last Line
The #1 culprit here is almost always using while (!file.eof()) as your loop condition. Here's why that breaks:
- The
eof()flag only gets set after you attempt to read past the end of the file. So when you finish reading the last valid line,eof()is still false—your loop runs one more time, tries to read an empty/invalid line, and gets stuck in a state where it can't progress but also doesn't triggereof().
Fix: Move the Read Operation Into the Loop Condition
Instead of checking eof() directly, put your line-read operation right in the while statement. This ensures the loop only runs if the read was successful:
std::string line; while (std::getline(file, line)) { // Only enter loop if a line was successfully read // Process the line... }
If you're reading tokens directly (not full lines), you can do the same:
std::string token; while (file >> token) { // Loop only if a token was successfully read // Process the token... }
2. Unhandled std::ios_base::failure Exception
This exception usually fires when an I/O operation fails (like reading invalid data, or trying to read from a closed/broken stream) and your stream is configured to throw exceptions on failure. Here's how to troubleshoot:
First, Check Your Stream's Exception Mask
If you added code like this somewhere, it tells the stream to throw an exception whenever failbit or badbit is set:
file.exceptions(std::ios::failbit | std::ios::badbit);
If you want to keep this behavior, you need to wrap your read logic in a try-catch block to handle the exception:
try { // Your read loop and processing code here } catch (const std::ios_base::failure& e) { LOG("I/O Error: " << e.what()); // Handle the error—clean up, return empty list, etc. }
Alternatively, you can disable exception throwing for the stream (the default behavior) and check error flags manually:
// Clear any existing exception mask (go back to default) file.exceptions(std::ios_base::goodbit);
Second, Validate Every Read Operation
Even without exceptions, invalid data (like a malformed "f" line in your .obj) will put the stream into a failed state, which can cause loops to hang or unexpected behavior. Always check if reads succeeded:
// Example for reading a vertex line float x, y, z; if (iss >> x >> y >> z) { // Check if the read was successful vertices.emplace_back(x, y, z); } else { LOG("Skipping invalid vertex line: " << line); // Optionally reset the stream state if you want to keep reading iss.clear(); }
Third, Always Verify File Open Success
Never skip checking if the file opened properly—this is a common source of hidden failures:
std::ifstream file(filepath); if (!file.is_open()) { LOG("Failed to open file: " << filepath); return {}; // Or throw an error, depending on your setup }
Example Improved read_mesh Function
Here's a trimmed-down version incorporating all these fixes:
#include <fstream> #include <sstream> #include <vector> #include <stdexcept> #include "Primitive.h" std::vector<Primitive> read_mesh(const std::string& filepath) { std::vector<Primitive> primitives; std::vector<glm::vec3> vertices; // Replace with your vertex type if needed std::ifstream file(filepath); // Check file open success if (!file.is_open()) { LOG("Error: Could not open mesh file at " << filepath); return primitives; } std::string line; // Correct loop condition: only run if line is read successfully while (std::getline(file, line)) { std::istringstream iss(line); std::string token; // Skip empty lines if (!(iss >> token)) continue; if (token == "v") { float x, y, z; if (iss >> x >> y >> z) { vertices.emplace_back(x, y, z); LOG("Read vertex: (" << x << ", " << y << ", " << z << ")"); } else { LOG("Warning: Invalid vertex line ignored: " << line); } } else if (token == "f") { int idx1, idx2, idx3; if (iss >> idx1 >> idx2 >> idx3) { // Convert .obj's 1-based index to 0-based glm::vec3 v1 = vertices[idx1 - 1]; glm::vec3 v2 = vertices[idx2 - 1]; glm::vec3 v3 = vertices[idx3 - 1]; primitives.emplace_back(v1, v2, v3); LOG("Read triangle from vertices: " << idx1 << ", " << idx2 << ", " << idx3); } else { LOG("Warning: Invalid face line ignored: " << line); } } // Add handling for vt/vn lines if needed } // Check why loop exited if (file.bad()) { LOG("Fatal error reading file: " << filepath); throw std::runtime_error("I/O failure during mesh read"); } else if (!file.eof()) { LOG("Warning: Stopped reading file early (possible invalid data)"); } LOG("Successfully loaded " << primitives.size() << " primitives"); return primitives; }
内容的提问来源于stack exchange,提问作者Bram Van Asschodt

