Reputation: 263270
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
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
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
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
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