Как вы можете писать все, кроме первых N строк потока, не повреждая поток назначения?
Например, следующее работает отлично, когда источник имеет n-1 или меньше строк и когда источник имеет n + 1 или более строк, но иногда терпит неудачу, когда источник имеет ровно n строк:
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");
}
С источником с ровно n линиями, пункт назначения иногда имеет набор сбоев.
Этот failbit устанавливается только тогда, когда источник имеет завершающую новую строку. Я тестирую это в Windows, и в шестнадцатеричном редакторе я вижу, что файлы с завершающей новой строкой и ровно n строк приводят к тому, что целевой поток имеет набор сбоев, но файлы с ровно n строками без завершающего конца строки не приводят к в наборе битбит. Все файлы, которые я тестирую, имеют символы новой строки как "\ r\n".
Я попытался открыть потоки в текстовом и двоичном режимах, но это не изменило поведение.
Если я изменю код, чтобы использовать std::copy
вместо записи rdbuf
, он работает независимо от того, существует ли завершающая rdbuf
. В соответствии с этим, эти два должны быть эквивалентными - почему пишет rdbuf сбой, а std :: copy преуспевает?
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");
}
Взгляните на стандарт:
basic_ostream<charT,traits>& operator<<(basic_streambuf<charT,traits>* sb);
7 Эффекты: ведет себя как неформатированная выходная функция (как описано в пункте 27.7.3.7, пункт 1). После того, как объект-
setstate(badbit)
будет построен, еслиsb
является нулевымsetstate(badbit)
(который можетios_base::failure
).
8 Получает символы изsb
и вставляет их в*this
. Символы считываются изsb
и вставляются до тех пор, пока не произойдет следующее:
- конец файла происходит во входной последовательности;
- вставка в выходную последовательность завершается с ошибкой (в этом случае символ, который нужно вставить, не извлекается);
- исключение возникает при получении символа из sb.
9 Если функция не вставляет никаких символов, она вызывает
setstate(failbit)
(которая можетios_base::failure
(27.5.5.4)). Если при извлечении символа былоfailbit
исключение, функция устанавливаетfailbit
в состояние ошибки, и еслиfailbit
вexceptions()
исключение пойманное исключается.
Таким образом, они эквивалентны, если будет скопирован хотя бы один символ.