文字列をバイナリ化(?)する

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Suikaba
記事: 194
登録日時: 10年前

文字列をバイナリ化(?)する

#1

投稿記事 by Suikaba » 8年前

こんにちわ!あけましておめでとうございます。
新年早々質問させて頂きます。
現在、プレイヤーの名前(std::string)をバイナリで保存しようとしてるのですが、
16進数に変換して保存する、再度読み込む時は16進数から文字とすれば良いのでしょうか。
std::stringまま保存できれば楽なのですが、できなさそうなので。

よろしくお願いします。


[追記]
ちなみに、セーブデータはこうなってます。

コード:

struct StSaveData
{
    long score[3][5][10];
    ...
    std::string player_name[3][5][10];
    ...
};
一応そのままで保存、読み込みができたのですが、

コード:

StSaveData save_data = {};
char* data = GetData(); // ファイルからデータ読み込み
memcpy( &save_data, data, sizeof(StSaveData) );
とすると、終了時にEnterCriticalSectionで例外が出てしまいました。(EnterCriticalSectionが悪いのではない)
試しでstd::stringをコメントアウトしてみたところ、例外が発生することなく正常に終了できたので、このまま保存しているのが悪いのかなと思い質問しました。
sizeofあたりが怪しいかなと思ってます。

[追記2]

コード:

#include <iostream>
#include <string>
#include <fstream>

int main()
{
	std::string str("ABC");
	{
		std::ifstream ifs( "test.dat", std::ios::binary | std::ios::in );
		if( ifs )
		{
			ifs.read( reinterpret_cast<char*>( &str ), sizeof( str ) );
		}
	}
	
	std::cout << str << std::endl;

	{
		std::ofstream ofs( "test.dat", std::ios::binary | std::ios::out | std::ios::trunc );
		ofs.write( reinterpret_cast<char*>( &str ), sizeof( str ) );
	}
	return 0;
}

簡単に言うと、これだと落ちちゃうのでどうにかならないかということです。
一文字一文字やっていくしかないのでしょうか。
よろしくお願いします。

アバター
lriki
記事: 88
登録日時: 9年前

Re: 文字列をバイナリ化(?)する

#2

投稿記事 by lriki » 8年前

std::string の使い方がちょっと怪しいのかなと思います。


まずは StSaveData 構造体についてですが、
std::string は 内部にポインタを持つクラスなので
構造体ごとそのままファイルに描きだすことはできません。

StSaveData 構造体に文字列を含めた上でをまるごとバイナリ化して保存する場合、
player_name は単純な char 型の配列にしておく必要があります。


ちなみに、std::string は以下のようにすると保存できます。

コード:

std::string str( "ABC" );
// 書き込み
{
    std::ofstream ofs( "test.dat", std::ios::binary |  std::ios::trunc );
    ofs << str << std::endl;
}
// 読み込み
{
    std::ifstream ifs( "test.dat", std::ios::binary );
    ifs >> str;
}

合わせて、std::ifstream、std::ofstream の文字列の保存の部分についても少し。

上に書いた通り、std::string はクラスです。
reinterpret_cast<char*>( &str ) のように、クラスインスタンスへのポインタをキャストしても
文字列へのポインタにはなりません。
std::string 内の文字列を取得するには c_str() メンバ関数を使います。

また、sizeof( str ) も文字列ではなくクラスのサイズを返してしまいます。
文字の数を返すには size() または length() メンバを使います。

例えば、以下のように書きなおすと上手く動作すると思います。

コード:

ifs.read( str.c_str(), str.size() + 1 );	// 終端文字を含む場合は + 1

Suikaba
記事: 194
登録日時: 10年前

Re: 文字列をバイナリ化(?)する

#3

投稿記事 by Suikaba » 8年前

なるほど・・・基礎知識が完全に欠落していたということですね。
ありがとうございました。
出なおしてきますw

beatle
記事: 1280
登録日時: 9年前
住所: 埼玉
連絡を取る:

Re: 文字列をバイナリ化(?)する

#4

投稿記事 by beatle » 8年前

梨樹 さんが書きました:例えば、以下のように書きなおすと上手く動作すると思います。

コード:

ifs.read( str.c_str(), str.size() + 1 );	// 終端文字を含む場合は + 1
c_strメソッドで返してくれる文字列は読み込み専用ですから書きこんではいけませんし,そもそも上の行はコンパイルできません.ご注意下さい.

アバター
lriki
記事: 88
登録日時: 9年前

Re: 文字列をバイナリ化(?)する

#5

投稿記事 by lriki » 8年前

申し訳ありません。ちょっと変なこと書いてました。
beatle さん、指摘ありがとうございます。

write の間違いです。
正しくは、以下のようになると思います。

コード:

// 読み込み
char buf[256];	// 十分に大きなバッファ
ifs.read( buf, 256 );
str = buf;

// 書き込み
ofs.write( str.c_str(), str.size() + 1 );

YuO
記事: 942
登録日時: 10年前
住所: 東京都世田谷区

Re: 文字列をバイナリ化(?)する

#6

投稿記事 by YuO » 8年前

std::stringやstd::wstringは,'\0'やL'\0'を要素として持ち得ます。
このため,std::stringを正確に再現するためには,ios_base::binaryを指定して開いたファイルに対して,data()からsize()個の要素を書き出すようにしないといけません。

また,可変長データを読み込むには,
  • 終端文字の定義
  • 長さ
のどちらかがないと,データの終端を認識できません。
なので,size()を先に書き出しておく必要があります。

閉鎖

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