web-dev-qa-db-ja.com

同じfstreamを使用した同じファイルの読み取りと書き込み

すでにいくつかのデータ(たとえば、8 kB)を含むファイルがあります。ファイルの最初から何かを読み、読み終えたところからデータを上書きしたい。だから私は次のコードを使おうとします:

_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を返しますが、2番目のコードはtrueを返し、実際には何も書き込まれません。

bytesCountを4096よりも小さい値(おそらく内部バッファのサイズ)に減らすと、2番目のbad()falseを返しますが、それでも何も書き込まれません。

seekp()行のコメントを外すと、書き込みが機能し始めます。bad()falseを返し、バイトが実際に書き込まれます。

ここでseekp()が必要なのはなぜですか?なぜそれなしでは機能しないのですか? seekp()はこれを行う正しい方法ですか?

Windows7でVisualStudio2012を使用しています。

15
svick

MSのfstreamライブラリがC _<stdio.h>_実装から継承する、更新モードで開かれたファイルに対する読み取り操作と書き込み操作の混合に関する制限に違反しています。

7.19.5.3/6のC標準(C99を引用していますが、この点ではC89と違いはありません)は次のように述べています。

ファイルが更新モード(上記のモード引数値のリストの2番目または3番目の文字として「+」)で開かれると、入力と出力の両方が関連するストリームで実行される場合があります。ただし、出力の直後に、フラッシュ関数またはファイル配置関数(fseek、fsetpos、またはrewind)の呼び出しを行わずに入力を行ってはなりません。また、入力の直後に呼び出しを行わずに出力を行ってはなりません。入力操作でファイルの終わりが検出されない限り、ファイル配置関数へ

(私の強調)。

したがって、C fseekに展開されるstream.seekp(1)ソリューションは正しいです。

GNU Cライブラリにはこの標準の制限がないため、投稿されたコードはGCCでビルドすると期待どおりに機能します。

MS _<fstream>_ライブラリは、この制限を継承する点でC++標準に準拠しています。 fstreamsは、_basic_filebuf<charT,traits>_を使用して実装されます。このテンプレートの(C++ 11)Standardの説明では、§27.9.1.1/ 2に、次のように書かれています。

クラスbasic_filebufのオブジェクトによって制御されるシーケンスの読み取りと書き込みの制限は、標準CライブラリFILEを使用した読み取りと書き込みの場合と同じです。

24
Mike Kinghan

結ばれたストリームを調べたいと思うかもしれません- http://www.cplusplus.com/reference/ios/ios/tie/

これにより、入力が読み取られるたびに出力が自動的にフラッシュされます。これは、個別の入力ストリームと出力ストリームが必要ですが、必要な動作だと思います。

1