了解二进制转换

时间:2013-02-03 17:54:23

标签: c++ binary binaryfiles

我正在编写一个资源文件,我想从各种常见文件中插入一堆数据,例如.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]格式的值,我稍后会用它来在屏幕上绘图。 我希望能够以我上面所述的二进制数据格式保存这个数组,然后读取它并填充这个数组。

我认为对这样的代码要求太多了,所以我想了解将这些值保存到二进制文件中然后回读以填充它需要知道的事情。

很抱歉很长的帖子!

edit2:我通过第一次编辑解决了我的问题...感谢宝贵的信息,我也知道了我想要的东西!

3 个答案:

答案 0 :(得分:1)

正在发生的事情是你正在复制内部 将数据表示到文件中,然后将其复制回来 进入记忆,只要程序在做,这个工作 写作是用相同版本的编译器编译的, 使用相同的选项。否则,它可能会也可能不会 工作,取决于你无法控制的任何事情。

我不清楚你要做什么,但格式如此 .jpg和.bmp通常指定他们想要的格式 要有不同的类型,你必须尊重这种格式。

答案 1 :(得分:1)

使用&amp;运算符,你得到一个指向变量内容的指针(把它想象成一个内存地址)。

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 *)&amp; randomValue时,你只需告诉“从这里获取具有char数据并写入sizeof(randomValue)字符的内存地址”。你不是在编写地址值本身,而是从内存位置写入内容(“原始二进制数据”)。

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

在这里,您需要提供char *类型数据,以null char(零)终止。但是,您将提供浮点值的原始字节。你的程序可能会在这里崩溃,因为cout会输入字符,直到找到终结符char - 它可能很快就会找不到。

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

float back = *(float*)charValue;

编辑:要保存二进制数据,您只需提供数据并写入()即可。不要使用&lt;&lt;运算符使用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)

此外,不要自己编写指针值(如果结构中有指针成员),因为一旦从文件读回,内存地址就不会指向任何有意义的数据。始终写入数据本身,而不是指针值。

答案 2 :(得分:0)

目前还不清楚你真正想做什么,所以我不能推荐一种解决你真正问题的方法。但是如果运行该程序实际上会导致程序中发出哔哔声或任何其他奇怪行为,我不会感到惊讶。

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

上面new返回的内存未初始化,但您正在尝试打印,就像它是一个以空字符结尾的字符串一样。未初始化的内存可能有铃声字符(当打印到终端时会发出哔哔声)或任何其他值,包括它可能没有空终止并且插入操作符进入流将溢出缓冲区,直到它找到null或程序崩溃访问无效内存。

您的代码中还有其他错误的假设,例如给定int *p = new int[10];,表达式sizeof(p)将是架构中指针的大小,而不是整数大小的10倍。