1

Using fstreams I have a file opened that contains numerous lines. Each contiguos set of 4 lines are such that: the first line is an int, the second and third are strings and fourth is a double. This sequence continues till EOF.

I'm attempting to load these lines into a struct array:

struct Library {
    int id;
    string title;
    string artist;
    double price;
};

and the code I'm trying to implement to load data into the struct is this:

const int LIMIT = 10
Library database[LIMIT];
ifstream file;
file.open("list.txt");

if(file) {
    while(!(file.eof()) && counter < LIMIT) {
        file >> database[counter].id;
        getline(file, database[counter].title;
        getline(file, database[counter].artist;
        file >> database[counter].price;
    }
} else {
   ...
}

// Using the following to debug output
for(int i = 0; i < counter; i++) {
    cout << "ID:     " << database[i].id << endl
         << "Title:  " << database[i].title << endl
         << "Artist: " << database[i].artist << endl
         << "Price:  " << database[i].price << endl
         << "-----------------------" << endl;
}

The file I'm trying to throw at this thing is

1234
Never Gonna Give You Up
Rick Astley
4.5
42
Thriller
Michael Jackson
32.1

The problem I'm having here is that between reading the id and title using file >> ... and getline(...) is that somewhere a newline bite is being introduced screwing up the output, which displays this monstrosity...

ID:     1234
Title:  
Artist: Never Gonna Give You Up
Price:  0
--------------------
ID:     0
Title:  
Artist:
Price:  0
--------------------

The solution is probably the most basic of solutions, but mainly because I can't figure out exactly what is going on with the newline bite I can't combobulate a phrase to shove into google and do my stuff there, and I'm at the stage where I've been looking at a problem so long, basic knowledge isn't working properly - such as how to handle basic input streams.

Any form of help would be much appreciated! Thanks in advance :)

SteppingHat
  • 1,078
  • 2
  • 20
  • 43

3 Answers3

4

This happens because the >> operator for the input stream only grabs part of a line, and does not always grab the newline character at the end of the line. When followed by a call to getline, the getline will grab the rest of the line previously parsed, not the line after it. There are a few ways to solve this: you can clear the buffer from the input stream after each read, or you can simply get all your input from getline and just parse the resulting strings into an integer or a double when you need to with calls to stoi or stod.

As a side note, you don't want to detect the end of your file the way you presently are. See why is eof considered wrong inside a loop condition?

Community
  • 1
  • 1
jaggedSpire
  • 4,212
  • 2
  • 21
  • 48
  • Would be possible to ignore the newline character at the beginning of `getline`? Purely because for various reasons I need to avoid `stoi` and `stod` and work purely based of input strategies. – SteppingHat Jun 08 '15 at 15:54
  • @SteppingHat certainly. A call to [`ignore`](http://en.cppreference.com/w/cpp/io/basic_istream/ignore) with an argument of a newline should work to extract all characters before the newline. (and the newline itself) – jaggedSpire Jun 08 '15 at 15:58
  • Beautiful! I'm still somewhat new-ish to the world of C++ so I'm still trying to wrap my head around input streams..its different from other languages. Thanks heaps! – SteppingHat Jun 08 '15 at 16:03
  • @SteppingHat Certainly. They can be slightly unintuitive as you've noticed, and the `eof` loop condition is a common mistake for beginners. – jaggedSpire Jun 08 '15 at 16:06
1

You can solve this problem by adding:

fflush(file);

everytime before you use getline(file, ...). Basically this will clear the input buffer before you use the getline() function. And fflush() is declared in the cstdio library.

Sid
  • 470
  • 1
  • 6
  • 19
  • Are you sure you mean `fflush`? The wiki states that the behavior of `fflush` for input streams is [undefined](http://en.cppreference.com/w/cpp/io/c/fflush). – jaggedSpire Jun 08 '15 at 16:00
  • I know that the behaviour of `fflush` is believed to be undefined. The idea was to clear the input buffer before using `getline` to get the expected input. I am not sure about how to do that without using `fflush`. – Sid Jun 08 '15 at 16:15
  • I do believe [`ignore`](http://en.cppreference.com/w/cpp/io/basic_istream/ignore) works. – jaggedSpire Jun 08 '15 at 16:16
  • `ignore` is worth a go, although sometimes you cannot determine exactly how many times you need to use that – Sid Jun 08 '15 at 16:18
  • You can supply it an argument to ignore every character read up until a specific character is found--using that with '\n' will clear until the end of the line. Any more complexity in the file beyond that would require additional logic, anyway. – jaggedSpire Jun 08 '15 at 16:21
0
file >> database[counter].id;

will read, in this case, a whitespace separated sequence of characters that is interpreted as an int. The newline is considered whitespace. You should now be sitting on that newline character, thus the getline() will read nothing -- successfully -- and increment the file position just past that.

You may be better off using getline() for each line and then separately interpreting the lines from the reading. For example, the first line read could be interpreted with a subsequent std::stoi() to get the integer representation from the string.

David Harrison
  • 256
  • 1
  • 9