1

I have a program that is given a file of a shape and reads it line by line to be able to put the shape into a 2D array. It's of an unknown size, so I have to count the rows as we go. Everything works fine, except I'm having trouble getting it to stop when there are empty lines trailing the input.

The piece of code in question is below:

while(cin.eof() != true){
        getline(cin, input);

        shape = shape + input;
        rows++;
}

For example this will count 3 rows:

===
===
===

and this counts 4:

===
===
===
(empty line)

I need my program to ignore the empty lines, regardless of how many there are. I've tried quite a few different things such as

if (!input.empty()){
        shape = shape + input;
        rows++;
}

or

if (input != " " && input[0] != '\0' && input[0] != '\n'){
       shape = shape + input;
       rows++;
}

These work if there is only one empty line, but if I had multiple empty lines it will only not count the very last one.

Shape and Input are both strings.

Nick____9
  • 73
  • 3
  • Maybe you should open the input in a hex editor and examine it. Sounds like those lines are not actually empty. Note that if the line is entirely whitespace and contains more than one character, your last test is not going to catch it. Maybe you need something like `if (std::all_of(input.begin(), input.end(), [](char c)->bool { return isspace(c); }))`. But then again, a line containing lots of whitespace isn't "empty". – paddy Apr 03 '20 at 03:01
  • Related: [Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong?](https://stackoverflow.com/questions/5605125/) – Remy Lebeau Apr 03 '20 at 03:06

1 Answers1

4

You have made a good choice to read a line at a time with getline(), but you are controlling your read loop incorrectly. See Why !.eof() inside a loop condition is always wrong.

Instead always control the continuation of your read loop based on the stream state resulting from the read function itself. In your case, you ignore the state after getline() and assume you have valid input -- which you won't when you read EOF. Why?

When you read the last line in your file, you will have read input, but the eofbit will not yet be set as you haven't reached the end-of-file yet. You loop checking cin.eof() != true (which it isn't yet) and then call getline (cin, input) BAM! Nothing was read and eofbit is now set, yet you blindly assign shape = shape + input; even though your read with getline() failed.

Your second issue is how do you skip empty lines? Simple. If input.size() == 0 the line was empty. To "skip" empty lines, just continue and read the next. To "quit reading" when the first empty line is reached, replace continue with break.

A short example incorporating the changes above would be:

#include <iostream>
#include <string>

int main (void) {

    std::string input{}, shape{};
    std::size_t rows = 0;

    while (getline(std::cin, input)) {      /* control loop with getline */
        if (input.size() == 0)              /* if .size() == 0, empty line */
            continue;                       /* get next */
        shape += input;                     /* add input to shape */
        rows++;                             /* increment rows */
    }

    std::cout << rows << " rows\n" << shape << '\n';
}

Also see: Why is “using namespace std;” considered bad practice? and avoid developing habits that will be harder to break later.

Example Use/Output

$ cat << eof | ./bin/inputshape
> ===
> ===
> ===
> eof
3 rows
=========

With a blank line at the end

$ cat << eof | ./bin/inputshape
> ===
> ===
> ===
>
> eof
3 rows
=========

Or with multiple blank lines:

$ cat << eof | ./bin/inputshape
> ===
> ===
> ===
>
>
> eof
3 rows
=========

(note: the eof used in input above is simply the heredoc sigil marking the end of input and has no independent significance related to the stream state eofbit or .eof(). It could just as well have been banannas, but EOF or eof are generally/traditionally used. Also, if you are not using bash or another shell supporting a heredoc, just redirect a file to the program, e.g. ./bin/inputshape < yourfile)

Look things over and let me know if you have further questions.


Edit Based On No Use of continue or break

If you can't use continue or break, then just turn the conditional around and only add to shape if input.size() != 0. For example:

    while (getline(std::cin, input)) {      /* control loop with getline */
        if (input.size() != 0) {            /* if .size() != 0, good line */
            shape += input;                 /* add input to shape */
            rows++;                         /* increment rows */
        }
    }

Exact same thing, just written a bit differently. Let me know if that works for you.

David C. Rankin
  • 69,681
  • 6
  • 44
  • 72
  • Thank you very much. I'm seeing how I can use what you've said here to help my program. Unfortunately, this is for an assignment and we can't use continue/break but I'll see how I go. – Nick____9 Apr 03 '20 at 06:14
  • 1
    Then just change it to `if (input.size() != 0) { shape += input; rows++; }` same thing, no `continue` - always more than one way to *skin-the-cat* `:)` – David C. Rankin Apr 03 '20 at 06:16