I started by writing rather a long comment on @Loki Astari's answer, but it's long enough (and, IMO, enough cleaner way to do the job) that it probably makes the most sense as an independent answer. In this case, you want something close to the standard loop, except that you keep reading as long as a read from one of the files succeeds. That being the case, @john is right, and it's best to avoid using eof()
as part of the loop condition.
std::string line1, line2;
static const char *prefixes[] = {"# ", "= "};
while (std::getline(file_1, line1) || std::getline(file_2, line2))
std::cout << prefixes[line1==line2] << line1 << "\n " << line2 << "\n";
Edit: @user1802174 raised a good point -- as it was, the loop didn't actually read data in parallel at all. Since it was using ||
which does short-circuit evaluation, when/if the read from the first file succeeded, it didn't read anything from the second file. Fortunately, he was wrong about one thing: it is fairly easy to fix. At least in this case, +
works fine, although we do have to explicitly cast the result to bool
. I've also added a fix for the fact that upon failure, getline
leaves the previous content of the string intact, so we need to explicitly clear the strings every iteration of the loop to get the desired behavior.
while (line1.clear(), line2.clear(),
(bool)std::getline(file_1, line1) + (bool)std::getline(file_2, line2))
{
std::cout << prefixes[line1==line2] << line1 << "\n " << line2 << "\n";
}
This time I did a quick test. File 1:
line1
line 2
File 2:
line 1
line 2
line 3
result:
# line1
line 1
= line 2
line 2
#
line 3
While obviously still not a full-blown diff utility, I think this is doing what was intended.
As in @Loki Astari's answer, this will basically act as if the file with fewer lines was padded with as many empty lines at the end as necessary to match the longer file.
As an aside, also note the use of "\n"
instead of std::endl
. Along with inserting a new-line, std::endl
also flushes the output buffer, which you almost certainly don't want in this case. Flushing the buffer still produces the correct results, but in many cases is likely to do so much more slowly.
Edit: As far as coding style goes, it probably is a bit better to write the loop as a for
loop instead of a while
:
for ( ; (bool)std::getline(file_1, line1) + (bool)std::getline(file_2, line2))
; line1.clear(), line2.clear())
{
std::cout << prefixes[line1==line2] << line1 << "\n " << line2 << "\n";
}
I personally see little real gain from using C++ style casts here. If I wanted to get away from using (bool)
, I'd probably use another well-known idiom (which, admittedly, many people also dislike):
for ( ; !!std::getline(file_1, line1) + !!std::getline(file_2, line2))
; line1.clear(), line2.clear())
{
std::cout << prefixes[line1==line2] << line1 << "\n " << line2 << "\n";
}
If somebody really objects to using a comma operator, this is easy to rewrite as:
while (!!std::getline(file_1, line1) + !!std::getline(file_2, line2))
{
std::cout << prefixes[line1==line2] << line1 << "\n " << line2 << "\n";
line1.clear();
line2.clear();
}
Personally, I don't consider that an improvement, but others may disagree.