istream operator >> not recognising '\n' character

600 Views Asked by At

I am basically reading a .txt file and storing values.

For example:

Student- Mark
Tennis

It will store Mark into memory as the studentName.

Now...If it is just

Student-
Tennis

Then it will work fine and produce an error.

However, if the file looks like this

Student-(space)(nothing here)
Tennis

It will store Tennis into memory as the studentName, when if fact it should store nothing and produce an error. I use '\n' character to determine if there is anything after the - character. This is my code...

istream& operator>> (istream& is, Student& student)
{   
    is.get(buffer,200,'-');
    is.get(ch);
    if(is.peek() == '\n')
    {
        cout << "Nothing there" << endl;
    }
    is >> ws;
    is.getline(student.studentName, 75);
}

I think it is because the is.peek() is recognizing white space, but then if I try removing white space using is >> ws, it removes the '\n' character and still stores Tennis as the studentName.

Would really mean a lot if someone could help me solve this problem.

2

There are 2 best solutions below

0
On

If you want to ignore whitespace but not '\n' you can't use std::ws as easily: it will skip over all whitespace and aside from ' ' the characters '\n' (newline), '\t' (tab), and '\r' (carriage return) are considered whitespace (I think there are actually even a few more). You could redefine what whitespace means for your stream (by replacing the stream's std::locale with a custom std::ctype<char> facet which has changed idea of what whitespace means) but that's probably a bit more advanced (as far as I can tell, there is about a handful of people who could do that right away; ask about it and I'll answer that question if I notice it, though...). An easier approach is to simply read the tail of the line using std::getline() and see what's in there.

Another alternative is create your own manipulator, let's say, skipspace, and use that prior to checking for newline:

std::istream& skipspace(std::istream& in) {
    std::istreambuf_iterator<char> it(in), end;
    std::find_if(it, end, [](char c){ return c != ' '; });
    return in;
}
// ...
if ((in >> skipspace).peek() != '\n') {
    // ....
}
2
On

You don't need to peek characters. I would use std::getline() and let it handle line breaks for you, then use std::istringstream for parsing:

std::istream& operator>> (std::istream& is, Student& student)
{   
    std::string line;
    if (!std::getline(is, line))
    {
        std::cout << "Can't read student name" << std::endl;
        return is;
    }

    std::istringstream iss(line);
    std::string ignore;
    std::getline(iss, ignore, '-');
    iss >> std::ws;
    iss.getline(student.studentName, 75);

    /*
    read and store className if needed ... 

    if (!std::getline(is, line))
    {
        std::cout << "Can't read class name" << std::endl;
        return is;
    }

    std::istringstream iss2(line);
    iss2.getline(student.className, ...);
    */

    return is;
}

Or, if you can change Student::studentName into a std::string instead of a char[]:

std::istream& operator>> (std::istream& is, Student& student)
{   
    std::string line;
    if (!std::getline(is, line))
    {
        std::cout << "Can't read student name" << std::endl;
        return is;
    }

    std::istringstream iss(line);
    std::string ignore;
    std::getline(iss, ignore, '-');
    iss >> std::ws >> student.studentName;

    /*
    read and store className if needed ... 

    if (!std::getline(is, student.className))
    {
        std::cout << "Can't read class name" << std::endl;
        return is;
    }
    */

    return is;
}