3

In this answer I have the following code:

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <iterator>
#include <limits>

using namespace std;

struct station{
    string _stationName;
    int _studentPass;
    int _adultPass;
};

std::istream& operator>>(std::istream& is, station& rhs){
    getline(is, rhs._stationName, ';');
    is >> rhs._studentPass >> rhs._adultPass;
    return is;
}

int main(){
    istringstream foo("4;\nSpadina;76 156\nBathurst;121 291\nKeele;70 61\nBay;158 158");

    foo.ignore(numeric_limits<streamsize>::max(), '\n');

    vector<station> bar{ istream_iterator<station>(foo), istream_iterator<station>() };

    for (auto& i : bar){
        cout << i._stationName << ' ' << i._studentPass << ' ' << i._adultPass << endl;
    }

    return 0;
}

It's output is:

Spadina 76 156

Bathurst 121 291

Keele 70 61

Bay 158 158

My expected output does not double space:

Spadina 76 156
Bathurst 121 291
Keele 70 61
Bay 158 158

I get the expected output if I change my operator>> to:

std::istream& operator>>(std::istream& is, station& rhs){
    if (is >> rhs._stationName >> rhs._adultPass){
        auto i = rhs._stationName.find(';');

        rhs._studentPass = stoi(rhs._stationName.substr(i + 1));
        rhs._stationName.resize(i);
    }
    return is;
}

This seems like a compiler bug or something, but that would be strange cause I'm seeing this behavior in both Visual Studio 2013 and gcc 4.9.2.

Can anyone explain this to me?

Community
  • 1
  • 1
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • 1
    `156\nBathurst` you aren't ignoring the `\n` in this part of the string. If all entries will be on one line, I would getline the whole line and then parse that string. – Neil Kirk Feb 13 '15 at 12:35
  • @NeilKirk Incidentally I tried using `getline` and splitting on [my first take of an answer](http://stackoverflow.com/revisions/28487797/1). It didn't work because the `istream_iterator`s have to read at the end of the stream, so when I get junk back from the `getline` and I try to split it things go poorly. I've switched to just extracting `ws` now instead. – Jonathan Mee Feb 13 '15 at 13:54
  • `string line; getline(is, line); ostringstream ss(line);` – Neil Kirk Feb 13 '15 at 14:11
  • @NeilKirk I'm not certain what you mean by this. If you have a better solution than extracting `ws` I'd love to see it in an answer, rather than a comment so I could accept it by the way. – Jonathan Mee Feb 13 '15 at 14:18
  • Put that code at the start of your operator, and then change your existing code to use `ss` instead of `is`. `ss` will contain `4;\nSpadina;76 156` but extract `4;\nSpadina;76 156\n` from the stream. – Neil Kirk Feb 13 '15 at 14:43
  • @NeilKirk That seems a lot more complicated than my updated solution: http://stackoverflow.com/a/28487797/2642059 My solution added `>> ws` not another `getline` and the construction of a new `string` and a new `stringstream`. Incidentally I think you intended `istringstream ss` not `ostringstream ss`. – Jonathan Mee Feb 13 '15 at 14:51
  • 1
    Yes I did mean istringstream. – Neil Kirk Feb 13 '15 at 15:10

1 Answers1

5

operator >> returning int does not discard the whitespace after it so when _adultPass is read, the next character in the stream is \n. If you then run getline that stops at ';', this newline character is read as well and stored at the beginning of the string.

StenSoft
  • 9,369
  • 25
  • 30