apebeast
apebeast

Reputation: 368

rdbuf() strange behavior

I'm trying to merge two files into one by simply using rdbuf(), but when merged, the file's first line is missing. The code is below:

void mergeFiles(ifstream &file1, ifstream &file2, ofstream &file3) {

    int num1;
    int num2;

    string firstLine_1;
    string firstLine_2;

    getline(file1, firstLine_1);
    num1 = atoi(firstLine_1.c_str());

    getline(file2, firstLine_2);
    num2 = atoi(firstLine_2.c_str());

    if (num1<num2) {
        file3 << file1.rdbuf() << "\n" << file2.rdbuf();
    } else {
        file3 << file2.rdbuf() << "\n" << file1.rdbuf();
    }
}

Upvotes: 0

Views: 804

Answers (2)

Wintermute
Wintermute

Reputation: 44063

Well, you pull out the first line of each file before concatenating the rest of them, so that's expected. You still have the lines, though, so simply print them in between:

if (num1<num2) {
    file3 << firstLine_1   << "\n"
          << file1.rdbuf() << "\n"
          << firstLine_2   << "\n"
          << file2.rdbuf();
} else {
    file3 << firstLine_2   << "\n"
          << file2.rdbuf() << "\n"
          << firstLine_1   << "\n"
          << file1.rdbuf();
}

Addendum: @BenVoigt rightly notes that if one of the files contains a single line that does not end with a newline, the output of this will be different from printing the plain file contents delimited by a single newline (there will be an extra newline in the middle or at the end).

On the basis that this case is recognizable because the first getline call will have set the end-of-file flag, this can be avoided as follows:

if (num1<num2) {
    file3 << firstLine_1;
    if(file1) { file3 << "\n" << file1.rdbuf(); }
    file3 << "\n";
    file3 << firstLine_2;
    if(file2) { file3 << "\n" << file2.rdbuf(); }
} else {
    file3 << firstLine_2;
    if(file2) { file3 << "\n" << file2.rdbuf(); }
    file3 << "\n";
    file3 << firstLine_1;
    if(file1) { file3 << "\n" << file1.rdbuf(); }
}

Since this is something of a mouthful and repetitive, I'd consider using a lambda to make it more concise:

auto printfile = [&file3](std::string const &firstline, std::istream &in) {
  file3 << firstline;
  if(in) { file3 << "\n" << in.rdbuf(); }
};

if(num1 < num2) {
  printfile(firstLine_1, file1);
  file3 << "\n";
  printfile(firstLine_2, file2);
} else {
  printfile(firstLine_2, file2);
  file3 << "\n";
  printfile(firstLine_1, file1);
}

For regular files like these, seeking back is also a possibility, but I feel that it is better to avoid giving up the ability to use code on pipes, sockets and other non-seekable streams without a compelling reason even if it seems like an unlikely use case.

Upvotes: 2

Ben Voigt
Ben Voigt

Reputation: 283733

file3 << filen.rdbuf()

Copies from the current position in the filen stream forward, into file3. The current position is after the first line, since you just read it.

Seek back to the beginning and your missing line will be missing no longer.

Upvotes: 1

Related Questions