今天開始進入 Qt 的另一個部分:文件讀寫,也就是 IO。文件讀寫在很多應用程序中都是需要的。Qt 通過 QIODevice 提供了 IO 的抽象,這種設(shè)備(device)具有讀寫字節(jié)塊的能力。常用的 IO 讀寫的類包括以下幾個:
QFlie | 訪問本地文件系統(tǒng)或者嵌入資源 |
---|---|
QTemporaryFile | 創(chuàng)建和訪問本地文件系統(tǒng)的臨時文件 |
QBuffer | 讀寫 QByteArray |
QProcess | 運行外部程序,處理進程間通訊 |
QTcpSocket | TCP 協(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
更多建議: