0

.JPG、.BMP (たとえば) などのさまざまな一般的なファイルから大量のデータを挿入したいリソース ファイルを作成しており、それをバイナリにしたいと考えています。インデックス別に整理されたこれらのデータを後で取得するために何かをコーディングするつもりです。

float randomValue = 23.14f;

ofstream fileWriter;
fileWriter.open("myFile.dat", ios::binary);
fileWriter.write((char*)&randomValue, sizeof(randomValue));
fileWriter.close();
//With this my .dat file, when opened in notepad has "B!¹A" in it

float retrieveValue = 0.0f;

ifstream fileReader;
fileReader.open("myFile.dat", ios::binary);
fileReader.read((char*)&retrieveValue, sizeof(retrieveValue));
fileReader.close();

cout << retrieveValue << endl; //This gives me exactly the 23.14 I wanted, perfect!

これはうまく機能しますが、そこで何が起こっているのかを正確に理解したいと思います。randomValueのアドレスを char* に変換して、このアドレスの値をファイルに書き込みますか?

配列に対してこれを行う必要があり、これを行うことができないため、私も興味があります:

int* myArray = new int[10];
//fill myArray values with random stuff

fileWriter.open("myFile.dat", ios::binary);
fileWriter.write((char*)&myArray, sizeof(myArray));
fileWriter.close();

私が理解していることから、これはすべての配列ではなく、最初のアドレスの値をファイルに書き込むだけです。したがって、テストのために、変数をファイルに書き込む char* に単純に変換し、変数に戻して値を正しく取得しているかどうかを確認しようとしているので、次のようにします。

int* intArray = new int[10];
for(int i = 0; i < 10; i++)
{
    cout << &intArray[i]; //the address of each number in my array
    cout << intArray[i]; //it's value
    cout << reinterpret_cast<char*>(&intArray[i]); //the char* value of each one     
}

しかし、何らかの理由で、このコードを実行するとコンピューターが「ビープ音」を鳴らします。配列中に、これらを char* に保存し、int に変換しようとしていますが、期待した結果が得られず、非常に長い値が得られます。何かのようなもの:

float randomValue = 23.14f;

char* charValue = reinterpret_cast<char*>(&randomValue);
//charValue contains "B!¹A" plus a bunch of other (un-initiallized values?) characters, so I'm guessing the value is correct

//Now I'm here

charValue を randomValue に変換したいのですが、どうすればよいですか?

編集:以下の回答には貴重な情報がありますが、私の(元の)問題は解決しません。これらのタイプの変換をテストしていたのは、BMP、JPG、MP3 などのリソース ファイルの束を選択し、それらをいくつかの基準で整理された単一の .DAT ファイルに保存するコードを実行しているためです。わかった。

後で、このリソース ファイルを使用して、これらのコンテンツを読み込んで、コーディング中のプログラム (ゲーム) にロードします。

私がまだ考えている基準ですが、次のようなことが可能かどうか疑問に思っていました:

//In my ResourceFile.DAT
[4 bytes = objectID][3 bytes = objectType (WAV, MP3, JPG, BMP, etc)][4 bytes = objectLength][objectLength bytes = actual objectData]
//repeating this until end of file

次に、リソース ファイルを読み取るコードで、次のようなことを行います (未テスト)。

ifstream fileReader;
fileReader.open("myFile.DAT", ios::binary);
//file check stuff
while(!fileReader.eof())
{
    //Here I'll load
    int objectID = 0;
    fileReader((char*)&objectID, 4); //read 4 bytes to fill objectID

    char objectType[3];
    fileReader(&objectType, 3); //read the type so I know which parser use

    int objectLength = 0;
    fileReader((char*)&objectLength, 4); //get the length of the object data

    char* objectData = new char[objectLength];
    fileReader(objectData, objectLength); //fill objectData with the data

    //Here I'll use a parser to fill classes depending on the type etc, and move on to the next obj
}

現在、私のコードは元のファイル (BMP、WAV など) を処理してクラスに入力しています。これらのファイルのデータをバイナリ データ ファイルに保存する方法を知りたいです。たとえば、BMP データを管理するクラスには次のようなものがあります。

class FileBMP
{
    public:
        int imageWidth;
        int imageHeight;
        int* imageData;
}

ロードするときは、次のように呼び出します。

void FileBMP::Load(int iwidth, int iheight)
{
    int imageTotalSize = iwidth * iheight * 4;
    imageData = new int[imageTotalSize]; //This will give me 4 times the amount of pixels in the image

    int cPixel = 0;
    while(cPixel < imageTotalSize)
    {
        imageData[cPixel] = 0;     //R value
        imageData[cPixel + 1] = 0; //G value
        imageData[cPixel + 2] = 0; //B value
        imageData[cPixel + 3] = 0; //A value
        cPixel += 4;
    }
} 

したがって、ピクセルあたり [RGBA] の形式の値を含むこの 1 次元配列があり、後で画面に描画するために使用します。この配列だけを、上で述べたように計画しているバイナリ データ形式で保存し、それを読み取ってこの配列に入力できるようにしたいと考えています。

このようなコードは要求が多すぎると思うので、これらの値をバイナリ ファイルに保存し、それを読み戻して埋めるために知っておくべきことを理解したいと思います。

長文すみません!

edit2:最初の編集を行うことで問題を解決しました...貴重な情報をありがとう、私も自分が何をしたいのかを知ることができました!

4

3 に答える 3

1

& 演算子を使用すると、変数の内容へのポインターを取得できます (単なるメモリ アドレスと考えてください)。

float a = 123.45f;
float* p = &a; // now p points to a, i.e. has the memory address to a's contents.
char* c = (char*)&a; // c points to the same memory location, but the code says to treat the contents as char instead of float.

write() に (char*)&randomValue を指定したときは、単に「char データを持つこのメモリ アドレスを取得し、そこから sizeof(randomValue) 文字を書き込む」ように指示しました。アドレス値自体を書き込むのではなく、メモリのその場所の内容 (「生のバイナリ データ」) を書き込みます。

cout << reinterpret_cast<char*>(&intArray[i]); //the char* value of each one 

ここでは、null char (ゼロ) で終了する char* 型のデータを指定する必要があります。ただし、代わりに float 値の生のバイトを提供しています。cout はターミネータ文字が見つかるまで文字を入力するため、プログラムはここでクラッシュする可能性があります。これはすぐには見つからない可能性があります。

float randomValue = 23.14f;
char* charValue = reinterpret_cast<char*>(&randomValue);

float back = *(float*)charValue;

編集: バイナリ データを保存するには、データを提供して write() するだけです。ofstream/cout で << 演算子のオーバーロードを使用しないでください。例えば:

    int values[3] = { 5, 6, 7 };
struct AnyData
{
   float a;
   int b;
} data;

cout.write((char*)&values, sizeof(int) * 3); // the other two values follow the first one, you can write them all at once.
cout.write((char*)&data, sizeof(data)); // you can also save structs that do not have pointers.

構造体を作成する場合は、#pragma packコンパイラ ディレクティブを参照してください。コンパイラは、変数を特定のサイズ (int) に揃えます (パディングを使用します)。これは、次の構造体が実際に 8 バイトを必要とする可能性があることを意味します。

#pragma pack (push, 1)
struct CouldBeLongerThanYouThink
{
  char a;
  char b;
};
#pragma pack (pop)

また、ポインター値自体を書き込まないでください (構造体にポインター メンバーがある場合)。メモリ アドレスは、ファイルから読み戻されると意味のあるデータを指しません。ポインター値ではなく、常にデータ自体を書き込みます。

于 2013-02-03T18:41:46.810 に答える
1

何が起こっているかというと、データの内部表現をファイルにコピーしてから、それをメモリにコピーし直すということです. これは、書き込みを行うプログラムが同じバージョンのコンパイラで同じオプションを使用してコンパイルされている限り機能します. . そうしないと、制御できないさまざまな事柄に応じて、機能する場合と機能しない場合があります。

何をしようとしているのかははっきりしませんが、通常、.jpg や .bmp などの形式は、さまざまなタイプに必要な形式を指定しており、その形式を尊重する必要があります。

于 2013-02-03T18:18:27.897 に答える
0

あなたが本当に何をしたいのかわからないので、あなたの本当の問題を解決する方法はお勧めできません。しかし、プログラムを実行すると実際にビープ音やその他の奇妙な動作がプログラムで発生したとしても、私は驚かないでしょう。

int* intArray = new int[10];
for(int i = 0; i < 10; i++)
{
    cout << reinterpret_cast<char*>(&intArray[i]);
}

上記で返されたメモリは初期化されていませんが、null で終了する文字列であるかのように出力newしようとしています。その初期化されていないメモリには、ベル文字 (端末に出力されたときにビープ音が発生する)またはその他の値が含まれる可能性があります。これには、null 終端がない可能性があり、ストリームへの挿入演算子が null を見つけるまでバッファをオーバーランする可能性があります。または、プログラムが無効なメモリにアクセスしてクラッシュします。

たとえばint *p = new int[10];、式sizeof(p)が整数のサイズの 10 倍ではなく、アーキテクチャ内のポインターのサイズになる場合など、コードには他の誤った仮定があります。

于 2013-02-03T18:37:50.180 に答える