How can you write all but the first N lines of a stream without corrupting the destination stream?
For example, the following works fine when the source has n-1 or less lines and when source has n+1 or more lines, but sometimes fails when the source has exactly n lines:
void copy_all_but_first_n_lines(std::ifstream& source, std::ofstream& dest, size_t n)
{
for (size_t i=0; i<n; ++i)
{
std::string line;
std::getline(source, line);
}
if (source.good()) dest << source.rdbuf();
if (!dest.good()) throw std::runtime_error("destination turned bad after writing remainder of source");
}
With a source with exactly n lines, destination sometimes has the failbit set.
This failbit is only set when the source has a trailing newline. I am testing this on Windows, and in a hex editor I see that files with a trailing newline and exactly n lines cause the destination stream to have the failbit set, but files with exactly n lines without a trailing end of line don't result in the failbit set. All the files I'm testing with have newlines as "\r\n".
I tried opening the streams in both text and binary mode, but that didn't change the behavior.
If I change the code to use std::copy
instead of writing the rdbuf
, it works regardless of whether or not there is a trailing newline. According to this, these two should be equivalent -- why is writing rdbuf failing while std::copy succeeds?
void copy_all_but_first_n_lines(std::ifstream& source, std::ofstream& dest, size_t n)
{
for (size_t i=0; i<n; ++i)
{
std::string line;
std::getline(source, line);
}
if (source.good())
{
std::istreambuf_iterator<char> begin(source);
std::istreambuf_iterator<char> end;
std::ostreambuf_iterator<char> destination(dest);
std::copy(begin, end, destination);
}
if (!dest.good()) throw std::runtime_error("destination turned bad after writing remainder of source");
}