std::istreamを使用した、バイナリーデータの変数格納ができない。

[戻る]
AUTOEXEC 2008/02/05(火) 14:37:27
AUTOEXECと申します。表題の件、質問させてください。
当方の環境は、WinXP(SP2) VC++2003です。

バイナリーファイル(たとえば画像ファイル等)を一旦メモリ上にバッファリングし、
さらに、フォーマットに準じた構造体メンバに格納する際、
std::istreamを使用して、スマートな実装を行いたい(※)と考えています。

 (※)
 下記のように、operator>>を使用して解決したいと考えています。
  iss:       メモリ上のバッファを入力とするストリーム
  member1〜4:フォーマット上のサイズに合わせたメンバ
 
  iss >> member1 >> member2 >> member3 >> member 4;

別方法(単純なメモリコピーの繰り返し)では、要件を実現しているのですが、
std::istreamでよりスマートに実装する方法があるとのことで、
コーディングの効率化のため、調査しています。

下記、サンプルプログラムにて、入力ストリームから情報が取れず、困っています。
大変お手数をおかけしますが、当方のどの点に問題あるか、ご指摘いただけますでしょ
うか?
よろしくお願い致します。

=================================================
#include <stdio.h>
#include <iostream>
#include <strstream>

//4バイトのデータを管理するクラス
class N4
{
private:
    int mVal;
public:
    N4()                    {Clear();};
    N4( const int& val )    {this->mVal = val;};
    ~N4()                    {};
    void    Clear()            {this->mVal    = 0;return;};

    friend std::istream & operator >>( std::istream &lhs , N4 &rhs ){
    return lhs >> rhs.mVal; };

};

// 実際には、上記N4クラスに似たクラスをhas-a関係で組み合わせて、
// ファイルフォーマットを構築しています。ここでは簡単のため、
// 上記N4クラスに、データを挿入することに着目します。

void    main(){

    char buf[4] = { 127, 126, 125, 124 };    //ファイルのイメージとす
る。(仮)
    std::istrstream istr( buf, 4 );            //入力ストリー
ム作成
    N4    n4 = -1;                        
    //N4型生成
    istr >> n4;                        
        //N4型へストリームから入力←うまくいかない

    return;
}

=================================================

επιστημη [HomePage] 2008/02/05(火) 15:48:44
#include <iostream>
#include <sstream>

class N4 {
private:
    int mVal, nVal;
public:
    N4()                    {Clear();};
    N4( const int& val )    {this->mVal = val;};
    ~N4()                    {};
    void    Clear()            {this->mVal    = 0;return;};
    void dump() const {
      const char* p = reinterpret_cast<const char*>(&mVal);
      for ( int i = 0; i < 4; ++i ) {
        std::cout << static_cast<int>(*p++) << ' ';
      }
      std::cout << std::endl;
    }

    friend std::istream & operator >>( std::istream &lhs , N4 &rhs ) { 
      char* p = reinterpret_cast<char*>(&rhs.mVal);
      lhs.get(*p++);
      lhs.get(*p++);
      lhs.get(*p++);
      lhs.get(*p++);
      return lhs;
   }

};

int main() {
    char buf[4] = { 127, 126, 125, 124 };
    std::istringstream istr(std::string(buf,4));
    N4    n4(0);
    istr >> n4;                        
    n4.dump();
}

AUTOEXEC 2008/02/05(火) 18:25:12
επιστημη様

AUTOEXECです。早速のご指摘ありがとうございます。
こちらのプログラムでは、operator>>定義と、
strstringを使用している点がまずいということが理解できました。
大変助かりました。

追加で質問させてください。
 事例でいただきましたプログラムの、operator>>は、
 1つめの変数では正しく動作するのですが、
 2つめ以降で、入力ストリームからデータを取得できないようです。
 
  funcA(): istr >> n >> m;       //連続で取得       
  funcB(): istr >> n; istr >> m; //分割で取得

  ↑双方共に、nだけデータが取得でき、mでは取得できませんでした。
 
 重ね重ねお手数をおかけしますが、
 どの点に問題あるか、ご指摘いただけますでしょうか?

======================================================
#include <iostream>
#include <sstream>    //istringstream
using namespace std;

class N4
{
private:
    int mVal;
public:
    N4()                    {Clear();};
    N4( const int& val )    {this->mVal = val;};
    ~N4()                    {};

    void    Clear()            {this->mVal    = 0;return;};
    void    dump()const{
        const char* p = (char*)(&mVal);
        for( int i=0; i<4; i++ ){
            std::cout << static_cast<int>(*p++) << ' ';
        }
        std::cout << std::endl;
    }

    friend std::istream & operator >>( std::istream &lhs , N4 &rhs ){
        char* p = reinterpret_cast<char*>(&rhs.mVal);
        lhs.get(*p++);    //p[0]を取得し、ポインタをずらず。
        lhs.get(*p++);    //p[1]を取得し、ポインタをずらず。
        lhs.get(*p++);    //p[2]を取得し、ポインタをずらず。
        lhs.get(*p++);    //p[3]を取得し、ポインタをずらず。
        return lhs >> rhs.mVal;
    };


};

void    funcA(){
    char buf[8] = { 127, 126, 0, 124, 123, 122, 0, 120 };
    std::istringstream istr(std::string(buf,8));
    N4  n(0);
    N4    m(0);
    istr >> n >> m;  
    n.dump();
    m.dump();
    return;
};

void    funcB(){
    char buf[8] = { 127, 126, 0, 124, 123, 122, 0, 120 };
    std::istringstream istr(std::string(buf,8));
    N4    n(0);
    istr >> n;
    n.dump();
    
    N4    m(0);
    istr >> m;
    m.dump();
    return;
};


void main(void)
{
    funcA();
    funcB();
    return;
}

επιστημη [HomePage] 2008/02/05(火) 20:12:18
    friend std::istream & operator >>( std::istream &lhs , N4 &rhs ){
        char* p = reinterpret_cast<char*>(&rhs.mVal);
        lhs.get(*p++);    //p[0]を取得し、ポインタをずらず。
        lhs.get(*p++);    //p[1]を取得し、ポインタをずらず。
        lhs.get(*p++);    //p[2]を取得し、ポインタをずらず。
        lhs.get(*p++);    //p[3]を取得し、ポインタをずらず。
×      return lhs >> rhs.mVal;
○      return lhs;
    };

AUTOEXEC 2008/02/06(水) 10:53:44
[[解決]]
επιστημη様

AUTOEXECです。解決しました。
当方のミス含め、ご回答(解答?)いただき、大変助かりました。
以下、まとめさせていただきます。

■ メモリ上にバッファしたバイナリデータを、     ■
■ std::istreamを使用してオブジェクトへ格納する方法 ■

  例) iss >> member1 >> member2 >> member3 >> member 4;

1.std::istringstreamを使用する。
    →std::istrstreamでは、NULL以降がカットされる。includeは、<sstream>を使用。

2.operator>>を、クラスごとに定義。
  →入力ストリームへの操作を実施。返値は左辺をそのまま返す。


以下、サンプルコード↓
====================================================
#include <iostream>
#include <iomanip>
#include <sstream>        //istringstream
#include <strstream>      //istrstream
using namespace std;

class N4
{
private:
    unsigned int mVal;
public:
    N4()                    {Clear();};
    N4( const int& val )    {this->mVal = val;};
    ~N4()                   {};

    void    Clear()         {this->mVal = 0;return;};

    void    dump()const{
        const unsigned char* p = (unsigned char*)(&mVal);
        cout << "[ ";
        for( int i=0; i<4; i++ ){
            cout.width(2); cout << uppercase;
            unsigned int A = static_cast<unsigned char>(*p++);
            cout << hex << A  << ' ';
        }
        cout << "]" << endl;
    }

    friend istream & operator >>( istream &lhs , N4 &rhs ){
        char* p = reinterpret_cast<char*>(&rhs.mVal);
        lhs.get(*p++);
        lhs.get(*p++)
        lhs.get(*p++);
        lhs.get(*p++);
        return lhs;
    };

};

void    Dump( string str, N4& n, N4& m ){
    cout << str << endl;
    cout << "  n:"; n.dump();
    cout << "  m:"; m.dump();
    cout << endl;
}

void    funcX( unsigned char* buf, int size, string str ){
    istrstream istr( (char*)buf );
    N4    n(0);N4    m(0);
    istr >> n >> m;
    Dump( str, n, m );
};

void    funcY( unsigned char* buf, int size, string str ){
    istringstream istr(string((char*)buf,8));
    N4  n(0);N4    m(0);
    istr >> n >> m;
    Dump( str, n, m );
};

void main()
{
    unsigned char buf1[8] = { 0xFF, 0xEE, 0xBB, 0xAA, 0x88, 0x44, 0x22, 0x11 };
    unsigned char buf2[8] = { 0xFF, 0xEE,    0, 0xAA, 0x88, 0x44,    0, 0x11 };

    cout << "istrstreamを使用した場合" << endl;
    funcX( buf1, sizeof(buf1), " 途中にNULLを含まない場合" );
    funcX( buf2, sizeof(buf2), " 途中にNULLを含む場合    " );

    cout << "istringstreamを使用した場合" << endl;
    funcY( buf1, sizeof(buf1), " 途中にNULLを含まない場合" );
    funcY( buf2, sizeof(buf2), " 途中にNULLを含む場合    " );

}
====================================================
ここまで↑

実行結果↓
====================================================
istrstreamを使用した場合
 途中にNULLを含まない場合
  n:[ FF EE BB AA ]
  m:[ 88 44 22 11 ]

 途中にNULLを含む場合
  n:[ FF EE  0  0 ]
  m:[  0  0  0  0 ]

istringstreamを使用した場合
 途中にNULLを含まない場合
  n:[ FF EE BB AA ]
  m:[ 88 44 22 11 ]

 途中にNULLを含む場合
  n:[ FF EE  0 AA ]
  m:[ 88 44  0 11 ]
====================================================
↑ここまで

※istringstreamを使用した場合、NULLを含むデータも、正しく読み込めました。

古いスレッドには追加発言できません。