QFile.write(myStruct) - 怎么样?

Gus*_*ard 1 c++ serialization qt qfile

我使用Qt开始,我坚持着一个问题理应相当长的一段时间了.我确信这只是我在C++中看不到的东西.无论如何,请看下面的简单代码并指出我做错了什么:

typedef struct FILEHEADER {
    char udfSignature[8];
    char fileName[64];
    long fileVersion;
    UNIXTIME fileCreation;
    UNIXTIME lastRebuild;
    FILEPOINTER descriptor;
} fileheader;

QFile f("nanga.dat");
    if(f.open(QIODevice::ReadWrite));

f.write(fileheader);
Run Code Online (Sandbox Code Playgroud)

Qt 5.2.0告诉我以下错误消息:

C:\sw\udb\udb\main.h:113: error: no matching function for call to
'QFile::write(FILEHEADER&)'
         file.write(header);
                          ^
Run Code Online (Sandbox Code Playgroud)

关于如何将这个结构写入a的任何建议QFile

谢谢

Rei*_*ica 5

鉴于其他人已经发现了明显的错误,让我们注意何时(并且只有在什么时候)可以做你想要做的事情.

头结构的内存中格式取决于平台和编译器.因此,只有当它的临时数据不超过应用程序的运行时间时,才能以您的方式存储标头.如果标题位于您退出之前删除的临时文件中,那么您就可以了.

另一方面,如果你试图"教导"这种永久存储二进制数据的方式 - 在应用程序退出后持续存在,那么你已经用脚射击了你的学生.用火箭筒,不能少.您根本不能保证下一版本的编译器将生成具有相同内存排列的结构字段的代码.或者其他一些编译器会这样做.

教学笔记

有几个值得解决的教学方面:编写可移植和可维护的文件格式的复杂性,以及编程语言C++的习惯用法.一种好的方法将利用两者之间固有的协同作用.

在我在公共论坛上看到的大多数代码中,固定长度的字符串缓冲区是缓冲溢出和不安全代码的网关药物.在教学方面,教给任何人都是一种灾难性的习惯.固定大小的缓冲区自动产生额外的问

  1. 由于存储填充而导致文件膨胀.

  2. 不可能存储任意长的字符串,从而强制丢失数据.

  3. 当太长的字符串必须被插入到短缓冲区中时,必须指定并测试"正确"行为.这也会引发一个错误.

既然你是用C++教学的,那么像其他技术人员用C++编写代码一样编写代码是个好主意.只是因为你可以把它写成像C一样,并且脑海中的C,那并不意味着它根本就是个好主意.与任何其他语言一样,C++也有成语 - 做事的方式既可以产生合适的代码,也可以获得其他人的理解和可维护性.

为此,人们应该使用QDataStream.它实现了自己的Qt内部可移植序列化格式.如果您需要从不使用Qt的代码中读取此格式,请参阅文档 - 二进制格式已记录并且稳定.对于简单的数据类型,它的执行方式与正确编写的C代码一样,除了默认情况下无论平台的字节顺序如何,文件始终是big-endian.

通过"简单地"将C结构写入磁盘完成的自制文件格式总是受到影响,因为默认情况下您无法控制数据在内存中的排列方式.由于您只是将结构的内存映像复制到文件中,因此您无法控制数据在文件中的表示方式.编译器的供应商处于控制之中,而不是你.

QDataStream并且QIODevice(实现QFile)必然抽象出一些复杂性,因为它们的目的是在没有用户必须编写大量样板代码来正确解决可移植性方面的情况下使用.以下是经常忽略将二进制数据写入文件的方面:

  1. 数字数据的字节顺序.
  2. 数据类型的大小.
  3. 在"连续"存储的数据之间填充.
  4. 文件格式的未来可扩展性和版本控制.
  5. 当存在固定大小的缓冲区时,缓冲区溢出和不可避免的数据丢失.

正确地解决它需要一些深谋远虑.但是,这是一个绝佳的机会,可以使用调试器跟踪代码流QDataStream,看看当字节被推送到文件缓冲区时会发生什么.这也是一个检查QDataStreamAPI的可移植性方面的机会.大多数代码存在的原因很充分,学生可以理解为什么这样做.

最终,学生可以重新实现一些小子集QDataStream(仅仅处理几个类型的便携),并同时使用Qt的和学生实现编写的文件可以被比较,以评估他们如何成功的任务.同样,QFile可以通过派生QIODevice和使用C文件API 来重新实现.

以下是Qt应该如何做到的.

// Header File

struct FileHeader { // ALL CAPS are idiomatically reserved for macros
  // The signature is an implementation detail and has no place here at all.
  QString fileName;
  // The file version is of a very dubious use here. It should only
  // be necessary in the process of (de)serialization, so ideally it should
  // be relegated to that code and hidden from here.
  quint32 fileVersion;
  QDataTime fileCreationTime;
  QDateTime lastRebiuildTime;
  // The descriptor is presumably another structure, it can be
  // serialized separately. There's no need to store a file offset for it
  // here.
};
QDataStream & operator<<(QDataStream& str, const FileHeader & hdr) {
QDataStream & operator>>(QDataStream& str, FileHeader & hdr) {

// Implementation File

static const quint32 kFileHeaderSignature = 0xC5362A99;
// Can be anything, but I set it to a product of two randomly chosen prime
// numbers that is greater or equal to 2^31. If you have multiple file
// types, that's a reasonable way of going about it.

QDataStream & operator<<(QDataStream& str, const FileHeader & hdr) {
  str << kFileHeaderSignature
      << hdr.fileName << hdr.fileVersion
      << hdr.fileCreationTime << hdr.lastRebuildTime;
  return str;
}

QDataStream & operator>>(QDataStream& str, FileHeader & hdr) {
  quint32 signature;
  str >> signature;
  if (signature != kFileHeaderSignature) {
    str.setStatus(QDataStream::ReadCorruptData);
    return;
  }
  str >> hdr.fileName >> hdr.fileVersion
      >> hdr.fileCreationTime >> hdr.lastRebuildTime;
  return str;
}

// Point of use

bool read() {
  QFile file("myfile");
  if (! file.open(QIODevice::ReadOnly) return false;
  QDataStream stream(&file);
  // !!
  // !!
  // !!
  // Stream version is a vitally important part of your file's binary format,
  // you must choose it once and keep it set that way. You can also store it
  // in the header, if you wish to go to a later version in the future, with the
  // understanding that older versions of your software won't read it anymore.
  // !!
  // !!
  // !!
  stream.setVersion(QDataStream::Qt_5_1);
  FileHeader header;
  stream >> header;
  ...
  if (stream.status != QDataStream::Ok) return false;
  // Here we can work with the data
  ...
  return true;
}
Run Code Online (Sandbox Code Playgroud)