簡體   English   中英

QFile.write(myStruct)-怎么樣?

[英]QFile.write( myStruct ) - how?

我從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);

Qt 5.2.0向我發送以下錯誤消息:

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

關於如何將該結構寫入QFile任何建議?

謝謝

鑒於其他所有人都發現了明顯的錯誤,讓我們注意何時(且僅當 )可以做您想做的事情。

頭結構的內存格式與平台和編譯器有關。 因此, 僅在臨時數據的持續時間不超過應用程序運行時的情況下,以您的方式存儲標頭是非常好的。 如果標題位於您要在退出前刪除的臨時文件中,則可以。

另一方面,如果您嘗試“教”這種永久存儲二進制數據的方法-直到應用程序退出后才能持續進行 ,您的學生就被槍殺了。 擁有火箭筒,同樣如此。 您完全不能保證編譯器的下一個版本將生成結構域中具有相同內存排列方式的代碼。 或其他編譯器會這樣做。

教學筆記

有幾個教學方面值得解決:編寫可移植且可維護的文件格式的復雜性,以及C ++編程語言的慣用用法。 一個好的方法將利用兩者之間的內在協同作用。

我在公共論壇上看到的大多數代碼中,固定長度的字符串緩沖區是緩沖溢出和不安全代碼的網關葯物。 在教學上,向任何人授課是一種災難性的習慣。 固定大小的緩沖區會自動產生其他問題:

  1. 由於填充的存儲,文件膨脹。

  2. 不可能存儲任意長的字符串,因此會導致數據丟失。

  3. 當必須將太長的字符串插入短緩沖區時,必須指定並測試“正確”的行為。 這也引發了一對一的錯誤。

由於您使用C ++進行教學,因此最好像其他熟練的C ++人一樣編寫代碼。 僅僅因為您可以像C一樣編寫代碼,然后像死腦筋一樣編寫代碼,但這並不意味着它是個好主意。 與其他任何語言一樣,C ++也有習慣用法-做事的方式會導致良好的代碼以及他人的良好理解和可維護性。

為此,應該使用QDataStream 它實現了自己的Qt內可移植序列化格式。 如果您需要從不使用Qt的代碼中讀取此格式, 請參考文檔 -二進制格式已記錄且穩定。 對於簡單的數據類型,它的執行方式與體面編寫的C代碼相同,只是默認情況下, 無論平台的字節序如何,文件始終為big-endian

通過“簡單地”將C結構寫入磁盤來完成的自制軟件文件格式始終會受到影響,因為默認情況下您無法控制數據在內存中的排列方式。 由於您僅將結構的內存映像復制到文件中,因此無法控制文件中數據的表示方式。 編譯器的供應商將由您控制,而不是您。

QDataStreamQIODevice (在QFile實現)必然會抽象出一些復雜性,因為它們的目標是可使用,而無需用戶編寫大量的樣板代碼來正確解決可移植性方面。 以下是將二進制數據寫入文件的常被忽略的方面:

  1. 數值數據的字節序。
  2. 數據類型的大小。
  3. 在“連續”存儲的數據之間進行填充。
  4. 文件格式的未來擴展性和版本控制。
  5. 當使用固定大小的緩沖區時,緩沖區溢出和不可避免的數據丟失。

正確處理它需要一些周到的考慮。 但是,這是一個絕好的機會,可以使用調試器通過QDataStream跟蹤代碼流,以查看將字節壓入文件緩沖區時實際發生的情況。 這也是檢查QDataStream API的可移植性方面的機會。 大部分代碼存在是有充分理由的,可以期望學生理解為什么要這樣做。

最終,學生可以重新實現QDataStream最小子集( 可移植地處理幾種類型),並且可以比較使用Qt和學生的實現編寫的文件,以評估他們在任務中的成功程度。 同樣,可以通過從QIODevice並使用C文件API重新實現QFile

這就是在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;
}

QFile具有write方法,可以接受任意字節數組。 您可以嘗試如下操作:

fileheader fh = { ...... };
QFile f("nanga.dat");
if(f.open(QIODevice::ReadWrite))
    f.write(reinterpret_cast<char*>(&fh), sizeof(fh));

但是請記住,通常,以這種方式存儲任何數據不是一個好主意。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM