fredoverflow
fredoverflow

Reputation: 263270

reading until user enters blank line

Suppose I want to read lines from the console and put those into a container until the user enters a blank line. I do not want that blank line ending up in my container, though. I can think of five different solutions:

a) break from loop

std::vector<std::string> container;
for (; ;)
{
    std::string line = get_input();
    if (line.empty()) break;
    container.push_back(line);
}

b) read before loop and inside loop

std::vector<std::string> container;
std::string line = get_input();
while (!line.empty())
{
    container.push_back(line);
    line = get_input();
}

c) read as part of loop condition, assignment version

std::vector<std::string> container;
std::string line;
while (!(line = get_input()).empty())
{
    container.push_back(line);
}

d) read as part of loop condition, sequence version

std::vector<std::string> container;
std::string line;
while (line = get_input(), !line.empty())
{
    container.push_back(line);
}

e) read too much, remove it after loop

std::vector<std::string> container;
std::string line;
do
{
    line = get_input();
    container.push_back(line);
}
while (!line.empty());
container.pop_back();

So, which solution would you prefer, and why? Which would be the easiest to understand for a beginner?

Upvotes: 1

Views: 2753

Answers (4)

Zac Howland
Zac Howland

Reputation: 15870

A modification to (d) makes it more efficient and follows what you are trying to do better.

std::vector<std::string> container;
std::string line;
do
{
    line = get_input();
    if (!line.empty())
    {
        container.push_back(line);
    }
    else
    {
        break;
    }
}
while (true);

Upvotes: 0

paul23
paul23

Reputation: 9445

I'd use method "d" actually:

-It shows in my opinion best what is done: first read the data, then if it isn't "good" data (empty line) stop reading the data. And everything is at the expected position (checking the data is in the loop-condition part, handling the data in the loop-body.

Mtheod "a" hides the condition checking & it is more difficult (in my humble opinion) to see the condition which "stops" the loop.

Upvotes: 2

Jerry Coffin
Jerry Coffin

Reputation: 490488

As you'd probably expect from me, I'd suggest a proxy:

class non_blank {
   std::string data;

   friend operator>>(std::istream &is, non_blank &n) {
       std::string temp;

       std::getline(is, temp);

       // I'm not writing this from home, so I'm going from memory.
       // The following line is probably a little wrong.
       is.setbit(std::ios::fail, temp.length()!=0);
       return is;
   }
   operator std::string() { return data; }
};

non_blank line;
while (infile >> line)
    container.push_back(line);

This has a side-effect that might be unexpected though: since it expects to read non-blank lines, it considers a blank line a failed conversion -- which means to read more from the stream afterwards, you have to clear the stream's fail bit. Since it does work by setting the stream's fail bit, you should also be able to use std::copy to read the input and it'll stop at the failed conversion.

Upvotes: 0

lijie
lijie

Reputation: 4871

I prefer (a). simple and reads quite naturally.

(b) repeats the line that gets the input.

(c) and (d) both use syntax that may be confusing to beginners (specifically, the comma not within a for statement or a definition, and assignment within a conditional). I'd probably prefer (c) over (d) though.

(e) is... inefficient. What if that last push_back caused a reallocation?

Upvotes: 2

Related Questions