I'm working on a project for class and I keep having the same error with a single line. I've tried asking classmates for help but I've been unable to get it working. When reading data from a file, the program is supposed to convert each line into information for a struct, those being the parity, species, and count of an animal. However, I keep getting an invalid argument exception every time on the line that is supposed to convert the final buffer value from a string to an integer. Here's my code. I have marked the line that causes the exception with "ERROR OCCURS HERE!".
EDIT: Here's the input file's content.
Oviparous Spider 10
Oviparous Bird 5
Viviparous Snake 20
Viviparous Dog 5
end
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <sstream>
using namespace std;
struct animal {
string parity;
string species;
int count;
};
void displayMenu() { // Displays Menu.
cout << "------------------------\n";
cout << "--- MENU ---\n";
cout << "--- ---\n";
cout << "--- 1) Print Data ---\n";
cout << "--- 2) Add Animal ---\n";
cout << "--- 3) Display Menu ---\n";
cout << "--- 4) Quit ---\n";
cout << "--- ---\n";
cout << "------------------------" << endl;
}
int main()
{
// Initialises variables.
int choice;
string fullLine;
// Reads input file.
ifstream data("C:\\temp\\input.txt"); // Gets file.
if (data.is_open()) {
vector<animal> animals;
int lines = count( // Gets total lines.
istreambuf_iterator<char>(data),
istreambuf_iterator<char>(),'\n');
--lines; // One is subtracted because of the "end" line.
animals.resize(lines); // Resizes to total animals.
for (int i = 0; i < lines; i++) {
getline(data, fullLine); // Gets line.
istringstream buf(fullLine); // Splits line into parts.
buf >> animals[i].parity; // Assigns to parity.
buf >> animals[i].species; // Assigns to species.
string conversion; buf >> conversion; animals[i].count = stoi(conversion); // Assigns to count. ERROR OCCURS HERE!
}
// Menu loop.
displayMenu();
while (true) {
cin >> choice;
if (choice == 1) { // Prints data.
cout << "The zoo contains: \n";
for (int i = 0; i < lines; i++) {
cout << animals[i].count << " " << animals[i].species << ", which are " << animals[i].parity << ".\n";
}
}
else if (choice == 2) { // TODO: Adds animal.
}
else if (choice == 3) { // Displays menu.
displayMenu();
}
else if (choice == 4) { // Quits program.
break;
}
}
}
}
Rewrite your file reading code like this. It's simpler than the code you have and it should work (although I haven't tested anything).
Instead of trying to pre-calculate the size of the
animals
vector. This code usespush_back
to add items to the vector as required. Much simpler. It also uses the convenient{ ... }
syntax to construct ananimal
object from the data that has been read. You could also define ananimal
constructor for the same purpose.It also explicitly tests for the
"end"
string to avoid adding that to the vector.The problem with your code (apart from the unnecessary complexity) is that in order to calculate the size of the vector you had to read the whole file. You can't then simply reread the file. Once a file has been read if you want to read it again, you need to take special actions to repostition the file at the beginning and clear any error state. Your code didn't do either of those things so it failed.