Reputation: 41
I am trying to read data from a text file formatted similarly to this:
knife, object, 0
bag, object, 15
kitchen, room, 400
Into an array composed of structures. Here is what I have so far, but it only reads the first element then returns garbage.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct itemlist
{
string type;
string selltype;
int price;
int diditsell=0;
};
int main()
{
string filename;
cout << "Please enter a file name. " << endl;
cin >> filename;
ifstream in(filename);
itemlist c[100];
for (int i=0;i<100;i++)
{
in >> c[i].type >> c[i].selltype >> c[i].price;
cout << c[i].type << endl;
cout << c[i].selltype << endl;
cout << c[i].price << endl;
}
}
I have tried to find examples that specifically suit what I am trying to do but implementing them has not fixed the problem. Any help would be greatly appreciated.
Upvotes: 3
Views: 2712
Reputation: 1
I had the same problem. A debug showed that it was reading the first array element but skipping to the second element and outputting the info. from the first element. This was fixed by making it read the first element twice. For example see below.
I had other input in the array for the player also. After that line was added everything worked great. I had to do that for every array that I read. I looked at the text file it was reading from and sure enough there is a blank line before the start of every array. I do not know why the program writing the file did that. I did not put a blank line before the array. Note: Instead of having it read the first array element twice, you could probably have it read a blank line instead.
for (int i = 0; i < PLAYER; i++)
{
getline(teamRosterIn, playerName[i]);
cout << playerName[i] << endl;
getline(teamRosterIn, playerName[i]);
cout << playerName[i] << endl;
}
Upvotes: 0
Reputation: 33931
The crux of the visible problem is that with
for (int i=0;i<100;i++)
the entire 100 element array will be printed out whether there was data in the file to be loaded into the array or not.
Probably the easiest way to do this is with a std::vector
. It's a dynamically sized array. As you add to it it gets bigger so you don't have to worry about it overflowing. We'll get back to it at the end.
The next thing you have to do is make sure you're reading the file successfully. Streams can be tested to see if they are valid.
if (in)
{
cout << "in is good!" << endl;
}
and the >>
operator returns a reference to the stream so you can
if (in >> data)
{
cout << "data is good!" << endl;
}
If the stream is still good after reading data, you know that at the very least the file read something into data that was of the correct type or could be converted into the correct type. You owe it to yourself to check the value read after reading it in to make sure the user didn't typo or go out of their way to crash the program. If you want to loop through a lot of stuff, like a file, you wind up with something like this:
while (in >> c[i].type >> c[i].selltype >> c[i].price)
If any of the reads failed the the stream will return false when tested and the loop will exit.
Looking at your source data you have spaces and commas to deal with. >>
only knows how to deal with spaces unless you're going to do a lot of extra work. What you will read in is
knife,
object,
0
and we don't want the comma. Fortunately, it's the last character so dealing with it is easy. A C++11 std::string
can be used like a stack and you can just pop the unwanted character off:
c[i].type.pop_back();
c[i].selltype.pop_back();
All together, this gives us a loop that looks like
ifstream in(filename);
itemlist c[100];
int i = 0;
while (in >> c[i].type >> c[i].selltype >> c[i].price)
{
c[i].type.pop_back();
c[i].selltype.pop_back();
cout << c[i].type << endl;
cout << c[i].selltype << endl;
cout << c[i].price << endl;
i++;
}
but this can overrun the end of the 100 element array, so we need to change the while
loop slightly:
while (i < 100 && in >> c[i].type >> c[i].selltype >> c[i].price )
If i
is greater than or equal to 100, the i < 100
case fails and the loop exits without even trying in >> c[i].type >> c[i].selltype >> c[i].price
and writing into the non-existent array slot.
Remember to keep the value of i
around because arrays are dumb. They don't know how full they are.
But with a vector
you don't need i
to count or to keep track of how full it is and you don't need to worry about overflowing the array until you run your computer out of RAM. What we do need is one temporary variable to read into and we're good to go.
vector<itemlist> c;
itemlist temp;
while (in >> temp.type >> temp.selltype >> temp.price)
{
temp.type.pop_back();
temp.selltype.pop_back();
cout << temp.type << endl;
cout << temp.selltype << endl;
cout << temp.price << endl;
c.push_back(temp);
}
Upvotes: 1