#pragma twice

KAB-studio > プログラミング > #pragma twice > 235 Version 12.12 構造体のファイル読み書き

#pragma twice 235 Version 12.12 構造体のファイル読み書き

前のページへ 表紙・目次へ 次のページへ

 Version 12.12
構造体のファイル読み書き

前回は、構造体のアライメントとパディングについて説明しました
構造体のメンバ変数が 8 バイトずつになっちゃう、それはプロジェクト
の設定で決められる、だよね
そう。それを踏まえて、構造体をファイルに書き込む場合について見て
みます

struct TEST_STRUCT_DOUBLE_2
{
    char m_ch;
    double m_d;
    char m_ch2;
};

void WriteStructToBinaryFile()
{
    // ファイルを開きます。
    FILE *pstFile
        = fopen( "StructBinary.aaa", "wb" );
    if( !pstFile )
    {
        TRACE( "ファイルが開けない!\n" );
        return;
    }

    // 構造体を準備します。
    TEST_STRUCT_DOUBLE_2 stTEST_STRUCT_DOUBLE_2;
    memset( &stTEST_STRUCT_DOUBLE_2, 0x00, sizeof( stTEST_STRUCT_DOUBLE_2 ) );
    stTEST_STRUCT_DOUBLE_2.m_ch  = 63;
    stTEST_STRUCT_DOUBLE_2.m_d   = 2.0;
    stTEST_STRUCT_DOUBLE_2.m_ch2 = 63;

    // 構造体を書き込みます。
    fwrite
        ( &stTEST_STRUCT_DOUBLE_2
        , sizeof( stTEST_STRUCT_DOUBLE_2 )
        , 1
        , pstFile 
        );


    // ファイルを閉じます。
    fclose( pstFile );
    return;
}

構造体は前回と同じ TEST_STRUCT_DOUBLE_2 です
 memset() で 0 にして、そのあとそれぞれの変数に入れて……あれ? 
この前って memset() に 0xAA セットしてなかったっけ
あれはパディングの部分を見るためのテストだから。本当はこうやって 
0 を入れるのが普通
確かに 0xAA ってちょっと変だもんね……
まぁ別に特別な理由はないから 0x00 以外でもいいんだけど、必要性がな
ければ 0x00 の方がいいかな
その次はファイルの書き込みね。使ってるランタイムは 
Version 12.09 ( No.232 ) と同じね
ここで重要なのは、 sizeof( stTEST_STRUCT_DOUBLE_2 ) の部分。前回
言ったように、ここにはパディングの分が入るから
見た目より大きなのになるってことだね
そういうこと。で、実際に、アライメントを 8 バイトにしてファイルに
書き込むと、次のようなデータになります
バイナリーモードで開いて、と

3F 00 00 00 00 00 00 00
00 00 00 00 00 00 00 40
3F 00 00 00 00 00 00 00

って、この前構造体の中身を見たときとほとんど同じじゃん。 AA が 00 
になったってだけで
そう、 memset() で 0xAA じゃなく 0x00 をセットしてるからね。でも、
それが重要
どゆこと?
構造体の中身がそのまま書き込まれるってことは、アライメントの設定次
第でファイルの構造が変わるってこと
あ……アライメントが 0 バイトだと、ファイルもパディングなくなった
状態で書き込まれるんだ
実際に試してみて
ほい。プロジェクトの設定で変えて、リビルドしてと

3F 00 00 00 00 00 00 00 40 3F

見事にパディングなくなっちゃった……
こんなふうに、アライメントの設定は実際のファイルの中身についても影
響します。さて、今度はこれを読み込んでみます

void ReadBinaryFiletoStruct()
{
    // ファイルを開きます。
    FILE *pstFile
        = fopen( "StructBinary.aaa", "rb" );
    if( !pstFile )
    {
        TRACE( "ファイルが開かない!\n" );
        return;
    }

    // 構造体を読み込みます。
    TEST_STRUCT_DOUBLE_2 stTEST_STRUCT_DOUBLE_2;
    fread
        ( &stTEST_STRUCT_DOUBLE_2
        , sizeof( stTEST_STRUCT_DOUBLE_2 )
        , 1
        , pstFile 
        );
    TRACE( "%d\n", stTEST_STRUCT_DOUBLE_2.m_ch  );
    TRACE( "%.20f\n", stTEST_STRUCT_DOUBLE_2.m_d   );
    TRACE( "%d\n", stTEST_STRUCT_DOUBLE_2.m_ch2 );
    // 63
    // 2.00000000000000000000
    // 63

    // ファイルを閉じます。
    fclose( pstFile );
    return;
}

ぱっと見いつも通り
ランタイムも今までのと同じだから特に問題ないよね
うん。ビルドして実行! うん、ちゃんとデータ取れてる!
こんなふうに、構造体を使って読み書きをすると、 fread() や fwrite() 
を何度も使わずに複数のデータを読み書きできるっていうメリットがありま

あ、そういえば。 Version 12.10 ( No.233 ) のときはめんどくさかった
もんね
でも、実際のところ、構造体を使ってファイルの読み書きをするのはデメ
リットの方が多いかも
え、そうなの?
まずはさっきのアライメントの問題。今は、書き込むときも読み込む時も
アライメントは 1 バイトにしていました
……だから読み込めたけど、もし今、アライメントを 8 バイトにして、
さっきのファイルを開くと……
やってみて
う、うん……

63
-925596313489040890000000000000000000000000000000000000(以下略)
-52

あ”
という感じに、まったく開くことができません
でも、今の状態でファイルに書き込めば、それは読み込めるんだよね
うん、アライメントが 8 バイトで書き込まれるからね。でも……たとえ
ばプログラムがバージョンアップしたときとかに、うっかりアライメントを
変えちゃうと
う”、前のが読み込めなくなる
それに、アライメントの設定はプロジェクトの設定で変わるから、ソース
ファイルとバイナリーファイルだけ送って、それをビルドして実行すると
アライメントが違って読み込めない……
っていう場合が多々あります。そのほかにも、構造体で読み書きするのに
はいくつか問題があります
たとえば?
たとえば、クラスの問題。クラスには、継承の関係とかもあって、単純に
ファイルには書き出せないようになっています
そういう複雑な情報ってファイルに出せないの?
普通には無理。ところが、 Version 7.07 ( No.127 ) で説明したよう

そうよ、構造体とクラスってほとんど同じなんじゃない?
だから、構造体だからって簡単にファイルに書き込むのはちょっと、って
いうことがあるから
やっぱ書き込むのはダメ、ってことね
ただ、複数のデータをまとめて書き込めるっていうのは便利だし、以前は
こうやって書き込むのは普通だったから
……結局どっちなの!?
えーっと……とりあえずは、構造体を直接書き込むのはしない方向で。で
も、仕組みとか方法は憶えておいて
その以前の、とかの関係?
そういうこと。昔のを使うときにはね。あと、構造体で書き込んでも、読
み込む時には構造体を使わない、っていう方法もあります
どゆこと?

void ReadStructBinaryFile()
{
    // ファイルを開きます。
    FILE *pstFile
        = fopen( "StructBinary.aaa", "rb" );
    if( !pstFile )
    {
        TRACE( "ファイルが開かない!\n" );
        return;
    }

    char ch;
    double d;
    char ch2;
    fread( &ch, sizeof( ch ), 1, pstFile );
    fread( &d, sizeof( d ), 1, pstFile );
    fread( &ch2, sizeof( ch2 ), 1, pstFile );
    TRACE( "%d\n", ch  );
    TRACE( "%.20f\n", d   );
    TRACE( "%d\n", ch2  );
    // 63
    // 2.00000000000000000000
    // 63

    // ファイルを閉じます。
    fclose( pstFile );
    return;
}

あ、 fread() でべたに読み込んでる
ただ、これはアライメントが 1 バイトの場合の処理
 8 バイトの場合には?
ランタイムに fseek() っていうのがあって、これだとファイルポインタ
を自由に移動できるから
パディングを飛ばせる?
そういうこと

/*
    Preview Next Story!
*/
バイナリーデータ編は次回で最後です
細かいルール多かったね
だから次回はそのまとめ
というわけで次回
< Version 12.13 バイナリーのまとめ >
につづく!
お、今回は全13回!
というか、文字列編が長すぎたね……
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。