3

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.

4
  • Check the read for success: if (in >> c[i].type >> c[i].selltype >> c[i].price) { print stuff; } else { handle error; } Commented Jan 24, 2017 at 23:11
  • Oh wait a sec! You need to parse comas and spaces? Yuck. That takes a std::getline (in, c[i].type, ','); and then some extra code to split off the space. Commented Jan 24, 2017 at 23:14
  • And you don't want to read in all 100 if you have less than 100 in the file. Are you allowed to use std::vector? Commented Jan 24, 2017 at 23:16
  • @user4581301 I'm being instructed to create a size of 100. There doesn't seem to be any issue using std::vector, but my C++ skills are not very sharp. Commented Jan 24, 2017 at 23:25

2 Answers 2

1

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);
}
Sign up to request clarification or add additional context in comments.

4 Comments

I truly appreciate all of your help. I'm having issues with this piece of code though. I notice nothing is being displayed in the program windows once the code is compiled and run. Since cout is being used, shouldn't it display something? And how would a call to this array work? When I simply call: 'cout << c[1].type << endl;' for example, the program simply crashes. Any advice?
@NickBearns No advice other than give your development environment's debugging software a crack at it. Very handy tool, the debugger. It should stop when the program crashes and allow you to inspect the state of the program. This will give you more information you can use to solve the problem or ask a really good, well-targetted new question.
I seem to have found the error. One entry in my file reads: water, not object, 0 The issue stems from the space in between the words in the second element. This causes issue later on because the use of in >> temp.type >> temp.selltype >> temp.price seems to be saving the price as the word "object", but since it is not an integer it cannot be stored properly.
@NickBearns Rats. If you control the data file format you can disallow spaces, but it's probably better to switch over to std::getline so you can correctly handle Two Handed Sword. A similar question came up tonight that you should be able to adapt. stackoverflow.com/a/41844212/4581301
0

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;
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.