Kludo
Kludo

Reputation: 45

Trying to read a text file into an array of structs in C++

Firstly, I'm using DEVC++, my goal of this code is to be able to read a text file into an array of structs. My text file is written like this: animalName:animalType:RegistrationNo:ProblemNo.

My issue with the below code is that it only seems to be running the while loop once.

I have looked up similar code but it uses to_string() and stoi but I don't think DEVC++ runs C++11 so I was wondering if there's an easy fix to my existing code or if there's another way to accomplish reading a text file which is made up of strings and ints

#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#define MAX 100
using namespace std;

struct Animal
{
   string animalName;
   string animalType;
   int Registration;
   int Problem;
};

int main()
{
   Animal ani[MAX];
   ifstream infile;
   int i = 0;

   infile.open("Animals.txt");
   if (!infile) {
       cout << "Unable to open file";
       exit(1);
   }

   int count = 0;
   while (infile.good()) {
       getline(infile, ani[i].animalName, ':');
       getline(infile, ani[i].animalType, ':');
       infile >> ani[i].Registration, ':';
       infile >> ani[i].Problem, '.';

       count++;
       i++;
   }

   infile.close();
   for (int i = 0; i < count; i++) {
       cout << ani[i].animalName << " ";
   }
   for (int i = 0; i < count; i++) {
       cout << ani[i].animalType << " ";
   }
   for (int i = 0; i < count; i++) {
       cout << ani[i].Registration << " ";
   }
   for (int i = 0; i < count; i++) {
       cout << ani[i].Problem<< " ";
   }

   return 0;
}

Upvotes: 2

Views: 325

Answers (1)

user4581301
user4581301

Reputation: 33931

You are misusing the comma operator.

infile >> ani[i].Registration, ':';` 

doesn't read and discard the ':', leading to bloody death... Sorry. Leading to a parsing error when

infile >> ani[i].Problem

tries to turn the ':' into an integer. This places infile into the fail state,

while (infile.good())

finds that infile isn't good, and exits the loop.

You will have to do something along the line of

std::string temp;
std::getline(infile, temp, ':');
ani[i].Registration = std::stoi(temp);

to read the stream up to the ':' delimiter into a std::string and then turn the string into an integer with std::stoi.

Documentation on std::stoi

That's the bulk of the bug. But...

while (infile.good())

tests that the stream is good BEFORE reading from it. This allows the stream to fail utterly while reading without any tests before the failed results are used.

while (getline(infile, ani[i].animalName, ':') &&
       getline(infile, ani[i].animalType, ':') &&
       getline(infile, temp1, ':') &&
       getline(infile, temp2, '.'))
{ // only goes into loop if everything was read
  // may also have to eliminate a newline here
    ani[i].Registration = std::stoi(temp1);
    ani[i].Problem = std::stoi(temp2); //exception if bad
    i++;
}

An even better approach is to make a >> operator overload for Animal because that lets you write a main loop that looks like

while (infile>> ani[i])
{ 
    i++;
}

And that's so simple that all rejoice. See What are the basic rules and idioms for operator overloading? for information on writing the >> operator and much more general wisdom.

Upvotes: 3

Related Questions