1

バイナリファイルをメモリに読み込んで、次のように使用しようとしています。

struct myStruct {
    std::string mystring; // is 40 bytes long
    uint myint1; // is 4 bytes long
};

typedef unsigned char byte;

byte *filedata = ReadFile(filename); // reads file into memory, closes the file
myStruct aStruct;
aStruct.mystring = filedata.????

オフセットを使用してバイナリファイルにアクセスし、そのオフセットで特定の長さを取得する方法が必要です。バイナリファイルデータをstd::stringに保存する場合、これは簡単ですが、バイナリデータを保存するためにそれを使用することは物事を行うための良い方法ではないと考えました。(filedata.substr(offset, len))

適度に広範囲な(IMO)検索では、関連性のあるものは何も見つかりませんでした。アイデアはありますか?必要だと思われる場合は、ストレージタイプを変更します(例:std :: vector)。

4

2 に答える 2

3

シリアル化ライブラリを使用しない場合は、各クラスにシリアル化サポートを追加することをお勧めします。

struct My_Struct
{
    std::string my_string;
    unsigned int my_int;
    void Load_From_Buffer(unsigned char const *& p_buffer)
    {
        my_string = std::string(p_buffer);
        p_buffer += my_string.length() + 1; // +1 to account for the terminating nul character.
        my_int = *((unsigned int *) p_buffer);
        p_buffer += sizeof(my_int);
    }
};

unsigned char * const buffer = ReadFile(filename);
unsigned char * p_buffer = buffer;
My_Struct my_variable;
my_variable.Load_From_Buffer(p_buffer);

その他の便利なインターフェースメソッド:

unsigned int Size_On_Stream(void) const; // Returns the size the object would occupy in the stream.
void Store_To_Buffer(unsigned char *& p_buffer); // Stores object to buffer, increments pointer.

テンプレートを使用すると、シリアル化機能を拡張できます。

void Load_From_Buffer(std::string& s, unsigned char *& p_buffer)
{
    s = std::string((char *)p_buffer);
    p_buffer += s.length() + 1;
}

void template<classtype T> Load_From_Buffer(T& object, unsigned char *& p_buffer)
{
  object.Load_From_Buffer(p_buffer);
}

編集1:構造を直接記述しない理由

CおよびC++では、構造体のサイズはそのメンバーのサイズの合計と等しくない場合があります。
コンパイラーは、メンバーがアドレス上に整列されるように、メンバー間に パディングまたは未使用のスペースを挿入できます。

たとえば、32ビットプロセッサは4バイト境界で物事をフェッチするのが好きです。構造体に1つcharあり、その後にが続く場合int、on相対アドレス1が作成さintれます。これは、4の倍数ではありません。コンパイラーは、相対アドレス4に並ぶように構造体をパディングintします。

構造体には、ポインターまたはポインターを含むアイテムが含まれる場合があります。
たとえばstd::string、文字列に3文字または300文字が含まれていても、タイプのサイズは40である場合があります。これには、実際のデータへのポインタがあります。

エンディアン。
マルチバイト整数では、最上位バイト(MSB)、別名ビッグエンディアン(人間が数値を読み取る方法)または最下位バイト(別名リトルエンディアン)などの一部のプロセッサが最初に使用されます。リトルエンディアン形式は、ビッグエンディアンよりも読み取りに必要な回路が少なくて済みます。

編集2:バリアントレコード

配列やコンテナなどを出力する場合は、コンテナ全体(未使用のスロットを含む)を出力するか、コンテナ内のアイテムのみを出力するかを決定する必要があります。コンテナ内のアイテムのみを出力するには、バリアントレコード手法を使用します。

バリアントレコードを出力するための2つの手法:数量の後にアイテムが続くか、アイテムの後に番兵が続く。後者は、センチネルがヌル文字であるCスタイルの文字列の記述方法です。

もう1つの手法は、アイテムの数量を出力し、その後にアイテムを出力することです。したがって、0、1、2、3、4、5の6つの数値がある場合、出力は次のようになります
。6//アイテムの

01
2
3
4
5

上記のLoad_From_Bufferメソッドでは、数量を保持するための一時的なものを作成し、それを書き出してから、コンテナーの各アイテムを追跡します。

于 2013-01-29T20:48:05.000 に答える
0

次のように、構造体のstd::ostream出力演算子とstd::istream入力演算子をオーバーロードできます。

struct Record {
    std::string name;
    int value;
};

std::istream& operator>>(std::istream& in, Record& record) {
    char name[40] = { 0 };
    int32_t value(0);
    in.read(name, 40);
    in.read(reinterpret_cast<char*>(&value), 4);
    record.name.assign(name, 40);
    record.value = value;
    return in;
}

std::ostream& operator<<(std::ostream& out, const Record& record) {
    std::string name(record.name);
    name.resize(40, '\0');
    out.write(name.c_str(), 40);
    out.write(reinterpret_cast<const char*>(&record.value), 4);
    return out;
}

int main(int argc, char **argv) {
    const char* filename("records");
    Record r[] = {{"zero", 0 }, {"one", 1 }, {"two", 2}};
    int n(sizeof(r)/sizeof(r[0]));

    std::ofstream out(filename, std::ios::binary);
    for (int i = 0; i < n; ++i) {
        out << r[i];
    }
    out.close();

    std::ifstream in(filename, std::ios::binary);
    std::vector<Record> rIn;
    Record record;
    while (in >> record) {
        rIn.push_back(record);
    }
    for (std::vector<Record>::iterator i = rIn.begin(); i != rIn.end(); ++i){
        std::cout << "name: " << i->name << ", value: " << i->value
                  << std::endl;
    }
    return 0;
}
于 2013-01-29T21:13:04.827 に答える