Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/147.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 解析二进制文件。什么是现代方式?_C++_Casting_Binary - Fatal编程技术网

C++ 解析二进制文件。什么是现代方式?

C++ 解析二进制文件。什么是现代方式?,c++,casting,binary,C++,Casting,Binary,我有一个二进制文件,有一些我知道的布局。例如,让格式如下: 2字节(无符号短)-字符串的长度 5字节(5个字符)-字符串-某些id名称 4字节(无符号整数)-跨步 24字节(6 x浮点-每3个浮点的2步)-浮点数据 该文件应如下所示(为了可读性,我添加了空格): 这里5-是2个字节:0x05 0x00。“你好”-5个字节,以此类推 现在我想读这个文件。目前我这样做: 将文件加载到ifstream 将此流读取到字符缓冲区[2] 将其强制转换为无符号short:无符号short len{*((

我有一个二进制文件,有一些我知道的布局。例如,让格式如下:

  • 2字节(无符号短)-字符串的长度
  • 5字节(5个字符)-字符串-某些id名称
  • 4字节(无符号整数)-跨步
  • 24字节(6 x浮点-每3个浮点的2步)-浮点数据
该文件应如下所示(为了可读性,我添加了空格):

这里5-是2个字节:0x05 0x00。“你好”-5个字节,以此类推

现在我想读这个文件。目前我这样做:

  • 将文件加载到ifstream
  • 将此流读取到
    字符缓冲区[2]
  • 将其强制转换为无符号short:
    无符号short len{*((无符号short*)缓冲区)}。现在我有了一根绳子的长度
  • 读取一个流到
    向量
    ,并从此向量创建一个
    std::string
    。现在我有了字符串id
  • 同样的方法读取接下来的4个字节并将它们转换为unsigned int。现在我有了一个跨步
  • 虽然文件末尾读取浮动的方式不同,但为每个浮动创建一个
    char bufferFloat[4]
    并强制转换
    *((float*)bufferFloat)
这行得通,但对我来说它看起来很难看。我可以直接读取
无符号短字符
浮点
字符串
等而不创建
char[x]
吗?如果没有,正确的方式是什么(我阅读了我正在使用的样式-是旧样式)

附言:当我写一个问题时,我脑海中出现了更清晰的解释——如何在
char[x]
中从任意位置强制转换任意数量的字节


更新:我忘了明确指出,编译时的字符串和浮点数据长度不知道,是变量。

< P> C方式,这将在C++中工作,将声明一个结构:

#pragma pack(1)

struct contents {
   // data members;
};
注意

  • 您需要使用pragma使编译器按照数据在结构中的外观对齐数据
  • 此技术仅适用于
然后将读取缓冲区直接强制转换为结构类型:

std::vector<char> buf(sizeof(contents));
file.read(buf.data(), buf.size());
contents *stuff = reinterpret_cast<contents *>(buf.data());
std::vector buf(sizeof(contents));
file.read(buf.data(),buf.size());
contents*stuff=reinterpret_cast(buf.data());
现在,如果您的数据大小是可变的,那么您可以将其分成几个块。要从缓冲区读取单个二进制对象,可以使用reader函数:

template<typename T>
const char *read_object(const char *buffer, T& target) {
    target = *reinterpret_cast<const T*>(buffer);
    return buffer + sizeof(T);
}
模板
常量字符*读取对象(常量字符*缓冲区,T和目标){
目标=*重新解释强制转换(缓冲区);
返回缓冲区+sizeof(T);
}

主要优点是这样的阅读器可以专门用于更高级的C++对象:

template<typename CT>
const char *read_object(const char *buffer, std::vector<CT>& target) {
    size_t size = target.size();
    CT const *buf_start = reinterpret_cast<const CT*>(buffer);
    std::copy(buf_start, buf_start + size, target.begin());
    return buffer + size * sizeof(CT);
}
模板
常量字符*读取对象(常量字符*缓冲区,标准::向量和目标){
size_t size=target.size();
CT常数*buf\U start=重新解释强制转换(缓冲区);
std::copy(buf_start,buf_start+size,target.begin());
返回缓冲区+大小*sizeof(CT);
}
现在在主解析器中:

int n_floats;
iter = read_object(iter, n_floats);
std::vector<float> my_floats(n_floats);
iter = read_object(iter, my_floats);
int n_浮动;
iter=读取对象(iter,n_浮动);
std::向量my_浮动(n_浮动);
iter=读取对象(iter,我的对象浮动);

注意:正如Tony D所观察到的,即使您可以通过
#pragma
指令和手动填充(如果需要)正确对齐,您仍然可能会遇到处理器对齐不兼容的情况,表现为(最佳情况)性能问题或(最坏情况)陷阱信号。只有当您可以控制文件的格式时,此方法才可能有意义。

您最好声明一个结构(使用1字节填充-如何-取决于编译器)。用那个结构写,用同样的结构读。在结构中只放置POD,因此没有<代码> STD::String 等,只使用文件I/O或其他进程间通信结构——使用正常<代码>结构> <代码>或<代码>类< /代码>,以供C++程序进一步使用。数据文件被打包成FORTRAN输出。路线都错了。我成功地使用了预处理器技巧,自动完成了您正在手动执行的操作:将原始数据从字节缓冲区解包到结构。其思想是在包含文件中描述数据:

BEGIN_STRUCT(foo)
    UNSIGNED_SHORT(length)
    STRING_FIELD(length, label)
    UNSIGNED_INT(stride)
    FLOAT_ARRAY(3 * stride)
END_STRUCT(foo)
现在,您可以定义这些宏来生成所需的代码,比如说结构声明,包括上面的内容,取消定义并再次定义宏来生成解包函数,然后是另一个包含,等等

注意:我第一次在gcc中看到这种技术用于抽象语法树相关的代码生成

如果CPP不够强大(或者这种预处理器滥用不适合您),请替换一个小的lex/yacc程序(或者选择您最喜欢的工具)


对我来说,在生成代码方面,而不是手工编写代码,至少在低级别的基础代码中这样做是多么令人惊讶。p> 由于您的所有数据都是可变的,因此您可以分别读取这两个块,并且仍然使用强制转换:

struct id_contents
{
    uint16_t len;
    char id[];
} __attribute__((packed)); // assuming gcc, ymmv

struct data_contents
{
    uint32_t stride;
    float data[];
} __attribute__((packed)); // assuming gcc, ymmv

class my_row
{
    const id_contents* id_;
    const data_contents* data_;
    size_t len;

public:
    my_row(const char* buffer) {
        id_= reinterpret_cast<const id_contents*>(buffer);
        size_ = sizeof(*id_) + id_->len;
        data_ = reinterpret_cast<const data_contents*>(buffer + size_);
        size_ += sizeof(*data_) + 
            data_->stride * sizeof(float); // or however many, 3*float?

    }

    size_t size() const { return size_; }
};
目前我这样做:

  • 将文件加载到ifstream

  • 将此流读取到字符缓冲区[2]

  • 将其强制转换为
    unsigned short
    unsigned short len{*((unsigned short*)buffer)}。现在我有了一根绳子的长度


最后一个风险是SIGBUS
(如果您的字符数组恰好从奇数地址开始,并且您的CPU只能读取在偶数地址对齐的16位值)、性能(一些CPU将读取未对齐的值,但速度较慢;其他CPU,如现代x86,则速度良好)和/或问题。我建议读这两个字符,然后你可以说<代码>(x(0)< P>如果不是学习目的,如果你有选择二进制格式的自由,你最好考虑使用诸如<强> <强>之类的东西,它将处理你的序列化并允许与其他平台和语言交互。

如果您不能使用第三方API,您可以查看
QDataStream
以获得灵感


    • 我个人是这样做的:

      // some code which loads the file in memory
      #pragma pack(push, 1)
      struct someFile { int a, b, c; char d[0xEF]; };
      #pragma pack(pop)
      
      someFile* f = (someFile*) (file_in_memory);
      int filePropertyA = f->a;
      
      非常有效
      const char* buffer = getPointerToDataSomehow();
      
      my_row data1(buffer);
      buffer += data1.size();
      
      my_row data2(buffer);
      buffer += data2.size();
      
      // etc.
      
      std::string s(the_size, ' ');
      
      if (input_fstream.read(&s[0], s.size()) &&
          input_stream.gcount() == s.size())
          ...use s...
      
      struct Data
      {
          uint32_t x;
          float y[6];
      };
      Data data;
      if (input_stream.read((char*)&data, sizeof data) &&
          input_stream.gcount() == sizeof data)
          ...use x and y...
      
      // some code which loads the file in memory
      #pragma pack(push, 1)
      struct someFile { int a, b, c; char d[0xEF]; };
      #pragma pack(pop)
      
      someFile* f = (someFile*) (file_in_memory);
      int filePropertyA = f->a;
      
      using Buffer = std::pair<unsigned char const*, size_t>;
      
      template <typename OffsetReader>
      class UInt16LEReader: private OffsetReader {
      public:
          UInt16LEReader() {}
          explicit UInt16LEReader(OffsetReader const or): OffsetReader(or) {}
      
          uint16_t read(Buffer const& buffer) const {
              OffsetReader const& or = *this;
      
              size_t const offset = or.read(buffer);
              assert(offset <= buffer.second && "Incorrect offset");
              assert(offset + 2 <= buffer.second && "Too short buffer");
      
              unsigned char const* begin = buffer.first + offset;
      
              // http://commandcenter.blogspot.fr/2012/04/byte-order-fallacy.html
              return (uint16_t(begin[0]) << 0)
                   + (uint16_t(begin[1]) << 8);
          }
      }; // class UInt16LEReader
      
      // Declined for UInt[8|16|32][LE|BE]...
      
      template <size_t O>
      class FixedOffsetReader {
      public:
          size_t read(Buffer const&) const { return O; }
      }; // class FixedOffsetReader
      
      // http://en.wikipedia.org/wiki/Zip_%28file_format%29#File_headers
      class LocalFileHeader {
      public:
          template <size_t O>
          using UInt32 = UInt32LEReader<FixedOffsetReader<O>>;
          template <size_t O>
          using UInt16 = UInt16LEReader<FixedOffsetReader<O>>;
      
          UInt32< 0> signature;
          UInt16< 4> versionNeededToExtract;
          UInt16< 6> generalPurposeBitFlag;
          UInt16< 8> compressionMethod;
          UInt16<10> fileLastModificationTime;
          UInt16<12> fileLastModificationDate;
          UInt32<14> crc32;
          UInt32<18> compressedSize;
          UInt32<22> uncompressedSize;
      
          using FileNameLength = UInt16<26>;
          using ExtraFieldLength = UInt16<28>;
      
          using FileName = StringReader<FixedOffsetReader<30>, FileNameLength>;
      
          using ExtraField = StringReader<
              CombinedAdd<FixedOffsetReader<30>, FileNameLength>,
              ExtraFieldLength
          >;
      
          FileName filename;
          ExtraField extraField;
      }; // class LocalFileHeader