Qt 二進制文件讀寫

2018-10-08 09:58 更新

二進制文件讀寫

今天開始進入 Qt 的另一個部分:文件讀寫,也就是 IO。文件讀寫在很多應用程序中都是需要的。Qt 通過 QIODevice 提供了 IO 的抽象,這種設(shè)備(device)具有讀寫字節(jié)塊的能力。常用的 IO 讀寫的類包括以下幾個:

QFlie訪問本地文件系統(tǒng)或者嵌入資源
QTemporaryFile創(chuàng)建和訪問本地文件系統(tǒng)的臨時文件
QBuffer讀寫 QByteArray
QProcess運行外部程序,處理進程間通訊
QTcpSocketTCP 協(xié)議網(wǎng)絡數(shù)據(jù)傳輸
QUdpSocket傳輸 UDP 報文
QSslSocket使用 SSL/TLS 傳輸數(shù)據(jù)

QProcess、QTcpSocket、QUdpSoctet 和 QSslSocket 是順序訪問設(shè)備,它們的數(shù)據(jù)只能訪問一遍,也就是說,你只能從第一個字節(jié)開始訪問,直到最后一個字節(jié)。QFile、QTemporaryFile 和 QBuffer 是隨機訪問設(shè)備,你可以從任何位置訪問任意次數(shù),還可以使用 QIODevice::seek() 函數(shù)來重新定位文件指針。

在訪問方式上,Qt 提供了兩個更高級別的抽象:使用 QDataStream 進行二進制方式的訪問和使用 QTextStream 進行文本方式的訪問。這些類可以幫助我們控制字節(jié)順序和文本編碼,使程序員從這種問題中解脫出來。

QFile 對于訪問獨立的文件是非常方便的,無論是在文件系統(tǒng)中還是在應用程序的資源文件中。Qt 同樣也提供了 QDir 和 QFileInfo 兩個類,用于處理文件夾相關(guān)事務以及查看文件信息等。這次我們先從二進制文件的讀寫說起。

以二進制格式訪問數(shù)據(jù)的最簡單的方式是實例化一個 QFile 對象,打開文件,然后使用 QDataStream 進行訪問。QDataStream 提供了平臺獨立的訪問數(shù)據(jù)格式的方法,這些數(shù)據(jù)格式包括標準的 C++ 類型,如 int、double 等;多種 Qt 類型,如 QByteArray、QFont、QImage、QPixmap、QString 和 QVariant,以及 Qt 的容器類,如 QList 和 QMap<K, T>。先看如下的代碼:


QImage image("philip.png");  

QMap<QString, QColor> map;  
map.insert("red", Qt::red);  
map.insert("green", Qt::green);  
map.insert("blue", Qt::blue);  

QFile file("facts.dat");  
if (!file.open(QIODevice::WriteOnly)) {  
    std::cerr << "Cannot open file for writing: " 
              << qPrintable(file.errorString()) << std::endl;  
    return;  
}  

QDataStream out(&file);  
out.setVersion(QDataStream::Qt_4_3);  

out << quint32(0x12345678) << image << map; 

這里,我們首先創(chuàng)建了一個 QImage 對象,一個 QMap<QString, QColor>,然后使用 QFile 創(chuàng)建了一個名為 "facts.dat" 的文件,然后以只寫方式打開。如果打開失敗,直接 return;否則我們使用 QFile 的指針創(chuàng)建一個 QDataStream 對象,然后設(shè)置 version,這個我們以后再詳細說明,最后就像 std 的 cout 一樣,使用 << 運算符輸出結(jié)果。

0x12345678 成為“魔術(shù)數(shù)字”,這是二進制文件輸出中經(jīng)常使用的一種技術(shù)。我們定義的二進制格式通常具有一個這樣的“魔術(shù)數(shù)字”,用于標志文件格式。例如,我們在文件最開始寫入 0x12345678,在讀取的時候首先檢查這個數(shù)字是不是 0x12345678,如果不是的話,這就不是可識別格式,因此根本不需要去讀取。一般二進制格式都會有這么一個魔術(shù)數(shù)字,例如 Java 的 class 文件的魔術(shù)數(shù)字就是 0xCAFE BABE(很 Java 的名字),使用二進制查看器就可以查看。魔術(shù)數(shù)字是一個 32 位的無符號整數(shù),因此我們使用 quint32 宏來得到一個平臺無關(guān)的 32 位無符號整數(shù)。

在這段代碼中我們使用了一個 qPrintable() 宏,這個宏實際上是把 QString 對象轉(zhuǎn)換成 const char *。注意到我們使用的是 C++ 標準錯誤輸出 cerr,因此必須使用這個轉(zhuǎn)換。當然,QString::toStdString() 函數(shù)也能夠完成同樣的操作。

讀取的過程就很簡單了,需要注意的是讀取必須同寫入的過程一一對應,即第一個寫入 quint32 型的魔術(shù)數(shù)字,那么第一個讀出的也必須是一個 quint32 格式的數(shù)據(jù),如


quint32 n;  
QImage image;  
QMap<QString, QColor> map;  

QFile file("facts.dat");  
if (!file.open(QIODevice::ReadOnly)) {  
    std::cerr << "Cannot open file for reading: " 
              << qPrintable(file.errorString()) << std::endl;  
    return;  
}  

QDataStream in(&file);  
in.setVersion(QDataStream::Qt_4_3);  

in >> n >> image >> map; 

好了,數(shù)據(jù)讀出了,拿著到處去用吧!

這個 version 是干什么用的呢?對于二進制的讀寫,隨著 Qt 的版本升級,可能相同的內(nèi)容有了不同的讀寫方式,比如可能由大端寫入變成了小端寫入等,這樣的話舊版本 Qt 寫入的內(nèi)容就不能正確的讀出,因此需要設(shè)定一個版本號。比如這里我們使用 QDataStream::Qt_4_3,意思是,我們使用 Qt 4.3 的方式寫入數(shù)據(jù)。實際上,現(xiàn)在的最高版本號已經(jīng)是 QDataStream::Qt_4_6。如果這么寫,就是說,4.3 版本之前的 Qt 是不能保證正確讀寫文件內(nèi)容的。那么,問題就來了:我們以硬編碼的方式寫入這個 version,豈不是不能使用最新版的 Qt 的讀寫了?

解決方法之一是,我們不僅僅寫入一個魔術(shù)數(shù)字,同時寫入這個文件的版本。例如:


QFile file("file.xxx");  
file.open(QIODevice::WriteOnly);  
QDataStream out(&file);  

// Write a header with a "magic number" and a version  
out << (quint32)0xA0B0C0D0;  
out << (qint32)123;  

out.setVersion(QDataStream::Qt_4_0);  

// Write the data  
out << lots_of_interesting_data;

這個 file.xxx 文件的版本號是 123。我們認為,如果版本號是123的話,則可以使用 Qt_4_0 版本讀取。所以我們的讀取代碼就需要判斷一下:


QFile file("file.xxx");  
 file.open(QIODevice::ReadOnly);  
 QDataStream in(&file);  

 // Read and check the header  
 quint32 magic;  
 in >> magic;  
 if (magic != 0xA0B0C0D0)  
     return XXX_BAD_FILE_FORMAT;  

 // Read the version  
 qint32 version;  
 in >> version;  
 if (version < 100)  
     return XXX_BAD_FILE_TOO_OLD;  
 if (version > 123)  
     return XXX_BAD_FILE_TOO_NEW;  

 if (version <= 110)  
     in.setVersion(QDataStream::Qt_3_2);  
 else 
     in.setVersion(QDataStream::Qt_4_0);  

 // Read the data  
 in >> lots_of_interesting_data;  
 if (version >= 120)  
     in >> data_new_in_XXX_version_1_2;  
 in >> other_interesting_data;

這樣,我們就可以比較完美的處理二進制格式的數(shù)據(jù)讀寫了。

本文出自 “豆子空間” 博客,請務必保留此出處 http://devbean.blog.51cto.com/448512/193918

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號