阅读二进制格式的问题[C ++]

时间:2018-06-17 17:59:04

标签: c++ file parsing binary

我需要解析具有以下规范的文件:

V-Ray网格格式说明(.vrmesh)

现在使用以下方法解析前两个值:

    char id[7];
    fgets( id, 7, file );
    uint32_t fileversion;
    char  bytesFileversion[4];
    fgets( bytesFileversion, 4, file );
    fileversion = bytesFileversion[3] | (bytesFileversion[2] << 8) | (bytesFileversion[1] << 16) | (bytesFileversion[0] << 24);

id和fileversion都可以打印好。

不幸的是,下一个字段是一个很长的int / uint64_t,我尝试用以下几种方式读取它:

    uint64_t lookUpTable;
    char  bytes[8];
    fgets( bytes, 8, file );
    //It should work... but it acually doesn't work
    lookUpTable =    static_cast<uint64_t> (bytes[7]) |
                    (static_cast<uint64_t> (bytes[6]) << 8)  |
                    (static_cast<uint64_t> (bytes[5]) << 16) |
                    (static_cast<uint64_t> (bytes[4]) << 24) |
                    (static_cast<uint64_t> (bytes[3]) << 32) |
                    (static_cast<uint64_t> (bytes[2]) << 40) |
                    (static_cast<uint64_t> (bytes[1]) << 48) |
                    (static_cast<uint64_t> (bytes[0]) << 56);

但是它没有用,我也发现了一个在Python https://github.com/bdancer/vray_tools/blob/master/VRayProxy.py中解析文件ok的实现,我无法理解为什么它可行,而我的实现却没有。

我试图修改那个py文件来读取/打印两个uint_32

    self.lookupOffset = self.binRead("I", 4)[0]
    self.report("  lookupOffseta = %i" % self.lookupOffset)
    self.lookupOffset = self.binRead("I", 4)[0]
    self.report("  lookupOffsetb = %i" % self.lookupOffset)

令人惊讶的是,我没有得到与使用

在C ++中完成相同的结果
    uint32_t lookUpTableA;
    char  bytesLookUpTableA[4];
    fgets( bytesLookUpTableA, 4, file );
    lookUpTableA = bytesLookUpTableA[3] | (bytesLookUpTableA[2] << 8) | (bytesLookUpTableA[1] << 16) | (bytesLookUpTableA[0] << 24);
    uint32_t lookUpTableB;
    char  bytesLookUpTableB[4];
    fgets( bytesLookUpTableB, 4, file );
    lookUpTableB = bytesLookUpTableB[3] | (bytesLookUpTableB[2] << 8) | (bytesLookUpTableB[1] << 16) | (bytesLookUpTableB[0] << 24);

所以它开始对我来说似乎是巫术。

提前感谢您的任何提示!

PS。这里引用的是一个格式为https://wetransfer.com/downloads/f055f43fcd82aa2c212d86482d4227a220180617175809/511208757a572c984ac1ad3a07f665e720180617175809/bfbc90

的二进制文件

2 个答案:

答案 0 :(得分:2)

前言

计算机可以在两种布局中订购多字节整数: Big Endian Little Endian 。 Big endian是最重要的字节,Little Endian是最不重要的字节 找出二进制文件具有哪种布局以及平台具有哪种布局。在开始编码之前非常重要的研究。

64位整数的二进制读取

我建议将64位数据直接读入64位整数:

uint64_t data;
data_stream.fread((char *) &data, sizeof(data); 

如果您的平台和数据文件具有相同的整数布局,则您的工作将在此处停止。

交换字节

如果您的平台字节顺序与数据不同,那么您将不得不重新排列字节:

uint64_t data; 
data_stream.fread((char *) &data, sizeof(data); 
uint64_t converted_data;
converted_data = (data & 0x000000000000FF) << 56
               | (data & 0x0000000000FF00) << 48
               | (data & 0x00000000FF0000) << 40
               | (data & 0x000000FF000000) << 32
               | (data & 0x0000FF00000000) << 24
               | (data & 0x00FF0000000000) << 16
               | (data & 0xFF000000000000) << 8;

在上面的代码段中,converted_datadata属于同一类型,因此不需要强制转换。也没有对齐问题。

答案 1 :(得分:1)

除了armadillo.vrmesh之外,这似乎可以正常地从http://help.chaosgroup.com/vray/help/maya/sdk22/vrmesh_format.html读取示例文件,因为它使用不同的id字符串。版本是一致的,偏移量在文件范围内并且始终接近结束。我找不到文件内容其余部分的简单描述,所以我没有尝试进一步解码。

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

struct VMeshInfo
{
    std::string filename;
    std::string id;
    uint32_t version;
    uint64_t lookup_offset;

    bool read(const std::string& name)
    {
        filename = name;
        std::ifstream f(filename, std::ios::binary);
        if (!f)
        {
            std::cerr << "Error opening file '" << filename << "'\n";
            return false;
        }
        char buffer[8] = { 0 };
        if (!f.read(buffer, 7))
        {
            std::cerr << "Error reading id from file '" << filename << "'\n";
            return false;
        }
        id = buffer;
        if (id != "vrmesh")
        {
            std::cerr << "id != 'vrmesh' in file '" << filename << "'\n";
            return false;
        }
        if (!f.read(reinterpret_cast<char*>(&version), sizeof version))
        {
            std::cerr << "Error reading version from file '" << filename << "'\n";
            return false;
        }
        if (!f.read(reinterpret_cast<char*>(&lookup_offset), sizeof lookup_offset))
        {
            std::cerr << "Error reading lookup_offset from file '" << filename << "'\n";
            return false;
        }
        return true;
    }

    void print(std::ostream& f)
    {
        f << "filename: " << filename << "\n";
        f << "id: " << id << "\n";
        f << "version: " << version << "\n";
        f << "lookup_offset: " << lookup_offset << "\n";
        f << "====================\n";
    }
};

int main()
{
    std::vector<std::string> files{ "cube.vrmesh", "cylinder_bend.vrmesh", "objects.vrmesh" };
    for (auto& filename : files)
    {
        VMeshInfo info;
        if (info.read(filename))
        {
            info.print(std::cout);
        }
        else
        {
            std::cerr << "Error reading file info!\n";
            continue;
        }
    }
    return 0;
}

输出:

filename: cube.vrmesh
id: vrmesh
version: 4096
lookup_offset: 798
====================
filename: cylinder_bend.vrmesh
id: vrmesh
version: 4096
lookup_offset: 271397
====================
filename: objects.vrmesh
id: vrmesh
version: 4096
lookup_offset: 68051
====================