Чтение и запись в один и тот же файл с использованием одного и того же потока

У меня есть файл, который уже содержит некоторые данные (скажем, 8 кБ). Я хочу что-то прочитать с начала файла, а затем перезаписать данные, начиная с того места, где я закончил чтение. Поэтому я пытаюсь использовать следующий код:

std::fstream stream("filename", std::ios::in | std::ios::out | std::ios::binary);

char byte;
stream.read(&byte, 1);

// stream.seekp(1);

int bytesCount = 4096;

auto bytesVec = std::vector<char>(bytesCount, 'c');
char* bytes = bytesVec.data();

std::cout << stream.bad() << std::endl;

stream.write(bytes, bytesCount);

std::cout << stream.bad() << std::endl;

Если я выполняю этот код, первый bad() возвращает false, а второй возвращает true, и ничего не записывается.

Если я уменьшу bytesCount до значения меньше 4096 (предположительно, размера некоторого внутреннего буфера), второй bad() возвращает false, но все равно ничего не записывается.

Если я раскомментирую строку seekp(), запись начнет работать: bad() возвращает false, и байты фактически записываются.

Почему здесь нужен seekp()? Почему он не работает без него? Правильно ли это seekp()?

Я использую Visual Studio 2012 для Windows 7.

Ответ 1

Вы падаете с ограничениями на смешивание чтения и записывать операции в файл, открытый в режиме обновления, что MS fstream библиотека наследует от реализации C <stdio.h>.

Стандарт C (я цитирую C99, но он не отличается в этом пункте от C89) в 7.19.5.3/6 состояния:

Когда файл открывается с режимом обновления ('+' в качестве второго или третьего символа в выше списка значений аргумента режима), как вход, так и выход могут выполняться на связанный поток. Тем не менее, вывод не должен сопровождаться вводом без промежуточный вызов функции fflush или функции позиционирования файла (fseek, fsetpos или перемотка назад), и вход не должен напрямую сопровождаться выходом без промежуточный вызов функции позиционирования файла, если только входная операция не встречает конечный результат, из файла.

(мой акцент).

Итак, ваше решение stream.seekp(1), которое переходит на C fseek, является правильным.

В библиотеке GNU C нет этого стандартного ограничения, поэтому ваш код, который опубликован, работает как и ожидалось при построении с помощью GCC.

Библиотека MS <fstream> совместима со стандартом С++ в наследовании это ограничение. fstream реализованы с использованием basic_filebuf<charT,traits>. В стандартной (С++ 11) стандартной учетной записи этого шаблона в § 27.9.1.1.2 он просто говорит:

Ограничения на чтение и запись последовательности, управляемой объектом класса basic_filebuf, те же, что и для чтения и записи с файлами F библиотеки Standard C.

Ответ 2

Вы можете посмотреть в привязанные потоки - http://www.cplusplus.com/reference/ios/ios/tie/

Это происходит каждый раз, когда ввод считывает вывод, будет автоматически очищаться, что я думаю, что вы хотите, хотя для этого требуется отдельный входной и выходной поток