std::ofstreamの挙動

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
すいか

std::ofstreamの挙動

#1

投稿記事 by すいか » 2ヶ月前

ファイルのデータを読み取った後、それを別のファイルに少しデータを加工して書き込む、というプログラムを書いたのですがうまくいきません。ファイルに書き込まれたデータがNULL,0になってしまいます(FavBinEdit,(メモ帳)で確認済み)

Microsoft Visual Studio Community 2017
Version 15.7.3
VisualStudio.15.Release/15.7.3+27703.2026
Microsoft .NET Framework
Version 4.7.02558

インストールされているバージョン:Community

Visual C++ 2017 00369-60000-00001-AA183
Microsoft Visual C++ 2017

流れは
ファイルのデータを丸々(部分的に)読み込む <-- OK,ウォッチでstd::vector<UCHAR>にデータがあることを確認できた
編集する必要がある場合は編集 <-- OK,編集後も異常なし
書き込む std::ofstream::writeに渡す前 <--OK,もちろん異常なし

といった感じです

書き込む部分はこんな感じです。

コード:

//コンテナを書き込む
template<class _Iterator>
inline void writeContainer( _Inout_ std::ofstream& Out, _In_ _Iterator Beg, _In_ _Iterator End )
{
	//データのサイズから書き込む
	try {
		const auto _WriteSize = ContainerSize( std::distance( Beg, End ) * sizeof( _Iterator::value_type ) );
		Out.write( reinterpret_cast<const char*>( &_WriteSize ), sizeof( ContainerSize ) );

		if (_WriteSize > 0) {

			info << _WriteSize << L',' << std::hex << std::showbase << (size_t)std::addressof( *Beg ); //191

			Out.write( reinterpret_cast<const char*>( std::addressof( *Beg ) ), _WriteSize );

//			for (ContainerSize i = 0; i < _WriteSize / sizeof( _Iterator::value_type ) && i < 3; ++i) {
			info << L"head data is " << *Beg << *( Beg + 1 ) << *( Beg + 2 ); //196,領域外へアクセスしたときは例外が投げられる
//			}

			trace << Out.fail(); //199
		}
	}
	catch (...)
	{
		//エラーを吐き出しておしまい
		error << L"cannot write container: name[" << typeid( _Iterator ).name() << L']';
	}
}

//
//コンテナを丸ごと書き込む
//
template<class Container>
inline void writeContainer( _Inout_ std::ofstream& Out, _In_ const Container& Data )
{
	writeContainer( Out, std::cbegin( Data ), std::cend( Data ) );
}

[log]
► スポイラーを表示

かずま

Re: std::ofstreamの挙動

#2

投稿記事 by かずま » 2ヶ月前

コンパイルできるように適当に宣言と main を追加し、実行してみました。

コード:

#include <iostream>   // cout, wcout, wcerr
#include <fstream>    // ofstream
#include <vector>     // vector

class ContainerSize {
	size_t size;
public:
	ContainerSize(size_t n) : size(n) {}
	operator int() const { return size; }
};

std::wostream& info = std::wcout;
std::wostream& error = std::wcerr;
std::ostream& trace = std::cout;

typedef unsigned char UCHAR;

//------------------------------------------------------------------
//コンテナを書き込む
template<class _Iterator>
inline void writeContainer( _Inout_ std::ofstream& Out, _In_ _Iterator Beg, _In_ _Iterator End )
{
	//データのサイズから書き込む
	try {
		const auto _WriteSize = ContainerSize( std::distance( Beg, End ) * sizeof( _Iterator::value_type ) );
		Out.write( reinterpret_cast<const char*>( &_WriteSize ), sizeof( ContainerSize ) );

		if (_WriteSize > 0) {
			info << _WriteSize << L',' << std::hex << std::showbase << (size_t)std::addressof( *Beg ); //191
			Out.write( reinterpret_cast<const char*>( std::addressof( *Beg ) ), _WriteSize );
//			for (ContainerSize i = 0; i < _WriteSize / sizeof( _Iterator::value_type ) && i < 3; ++i) {
				info << L"head data is " << *Beg << *( Beg + 1 ) << *( Beg + 2 ); //196,領域外へアクセスしたときは例外が投げられる
//			}
			trace << Out.fail(); //199
		}
	}
	catch (...) {
		//エラーを吐き出しておしまい
		error << L"cannot write container: name[" << typeid( _Iterator ).name() << L']';
	}
}

//
//コンテナを丸ごと書き込む
//
template<class Container>
inline void writeContainer( _Inout_ std::ofstream& Out, _In_ const Container& Data )
{
	writeContainer( Out, std::cbegin( Data ), std::cend( Data ) );
}
//------------------------------------------------------------------

int main()
{
	std::ofstream ofs("save.dat");
	if (!ofs) return 1;
	std::vector<UCHAR> v = { 'A', 'B', 'C', 'D', 'E' };
	writeContainer(ofs, v);
}
実行結果

コード:

5,0xa604c0head data is 0x410x420x430
出力ファイル save.dat のダンプ

コード:

0000000 05 00 00 00 41 42 43 44 45                       >....ABCDE<
0000011
sizeof(size_t) == 4、リトルエンディアンの環境で実行しているので、
先頭の 05 00 00 00 は 5であり、これはコンテナである v の size()に等しい。
何の問題もなく、想定通りに動いています。

どこが問題かを具体的なデータによる実行例で示して質問してください。

すいか

Re: std::ofstreamの挙動

#3

投稿記事 by すいか » 2ヶ月前

おはようございます。ご返信ありがとうございます。ofstream はこんな感じで開いております

コード:

//中略
std::wstring _NewFileName( ( ( lpFileName.empty() ) ? _get()._mdc.Name : lpFileName ) + L".temp" );
	std::ofstream _oStream( _NewFileName, std::ios::binary );
	std::ifstream _iStream( _get()._mdc.Name ); //ファイルの情報をそのまま使えるならば、そのまま使う

	if (!_oStream) {
		error << L"Cannot open file" << lpFileName;
		return;
	}
//後略
コンテナ ... データの[size(uint64_t),バイト単位] + データ本体
ファイルの構造は
ヘッダ部分 12byte(アライメントは一応詰めてあります)
束ねたファイル達の親ディレクトリ(コンテナ)
...
ファイル名(コンテナ)
ファイルのデータ(コンテナ)
...
フッター部分それぞれのファイル名等のコンテナに移動するための情報等、53byte(アライメントは詰めてある)
で出力されるデータが、(ログ上ではきちんと出力されているデータ) 例えば0x232から0x8f50Dまでといった感じでかなりの範囲のデータが飛んでいる(=0)ように見受けられるので質問させていただいた次第です。

ヘッダ部分,フッタ部分

コード:

#pragma pack(1)
	//ヘッダ
	struct FileInfo
	{
		char              Magic[4];          //MDC\0
		int64_t           EncodeInfoAddress; //EncodeInfoがあるファイルの位置
		std::wstring      RootDir;           //メインディレクトリ (\\で終わる)
	};

	//フッター
	struct Footer
	{
		char               Magic[4];         //CFI\0
		uint64_t           HashEncoded;      //圧縮された状態のハッシュ値
		uint64_t           HashPlain;        //展開された状態のハッシュ値
		uint64_t           OriginalFileSize; //もともとのファイルの大きさ
		uint64_t           PackedSize;       //圧縮されたファイルの大きさ
		unsigned char      EncodeCode;       //圧縮情報の開始を示す数値
		uint64_t           FileNameBeg;      //関連付けられたファイル名が始まる位置
		uint64_t           FileDataBeg;      //関連付けられたファイルのデータが始まる位置

		bool operator==( const Footer& Left ) const noexcept
		{
			return HashEncoded == Left.HashEncoded && HashPlain == Left.HashPlain;
		}

	};

#pragma pack(push)

すいか

Re: std::ofstreamの挙動

#4

投稿記事 by すいか » 2ヶ月前

自己解決しました。

コード:

//ファイルを開くとき
std::ifstream _iStream( _get()._mdc.Name, std::ios::binary); //ファイルの情報をそのまま使えるならば、そのまま使う
std::ios::binaryのフラグを指定するのをまるっきり忘れていました。読み込み側はてっきりうまく動いていると思い込んでいたので、チェックしてませんでした。(さっきチェックしたら案の定だった)
お騒がせしました。

返信

“C言語何でも質問掲示板” へ戻る