QT中QDataStream二進(jìn)制數(shù)據(jù)讀寫的實現(xiàn)
Qt中的QDataStream類為我們的程序提供了讀寫二進(jìn)制數(shù)據(jù)的能力。一個數(shù)據(jù)流如果是二進(jìn)制編碼的數(shù)據(jù)流,那么它肯定是與計算機(jī)的操作系統(tǒng)、CPU或者字節(jié)序無關(guān)的。例如,一個數(shù)據(jù)流是在一個運(yùn)行Windows系統(tǒng)的PC機(jī)上被寫入的,那么它照樣可以在一臺運(yùn)行Solaris的Sun SPARC的機(jī)器上被讀取出來。同樣,我們也可以使用QDataStream去讀寫原生的未編碼的二進(jìn)制數(shù)據(jù)。
QDataStream類實現(xiàn)了序列化C++的基本數(shù)據(jù)類型的功能,比如char,short,int,char* 等等。如果要序列化更復(fù)雜的數(shù)據(jù)類型,可以將復(fù)雜數(shù)據(jù)類型分解成獨(dú)立的基本數(shù)據(jù)類型分別進(jìn)行序列化。
一個數(shù)據(jù)流往往需要一個QIODevice配合使用。因為QIODevice代表了一個可以從中讀取數(shù)據(jù)或向其寫入數(shù)據(jù)的輸入輸出設(shè)備。我們最常常見的QFile文件類就是一種QIODevice。下面我們先分別看一個使用QDataStream進(jìn)行二進(jìn)制數(shù)據(jù)讀寫的例子。
write binary data to a stream:
QFile file("file.dat"); file.open(QIODevice::WriteOnly); QDataStream out(&file); // we will serialize the data into the file out << QString("the answer is"); // serialize a string out << (qint32)42; // serialize an integer
read binary data from a stream:
QFile file("file.dat"); file.open(QIODevice::ReadOnly); QDataStream in(&file); // read the data serialized from the file QString str; qint32 a; in >> str >> a; // extract "the answer is" and 42
每一項被寫入的數(shù)據(jù),都是按一種預(yù)定義的二進(jìn)制格式寫入的,改格式取決于具體每一項的類型。而QDataStream支持的類型包括QBrush,QColor,QDateTime等等。
特別注意,對應(yīng)整數(shù)來說,在寫入時最好轉(zhuǎn)換成Qt中的某種整數(shù)類型,讀取時也讀取為同樣的Qt整數(shù)類型。這可以確保得到正確大小的整數(shù)并且可以屏蔽掉不同編譯器和平臺之間的差異。舉個栗子,對于一個char* 字符串來說,先寫入一個32-bit的整數(shù)值,該值就是字符串的長度,包括'\0',緊接著是字符串中 的每一個字符,包括'\0'。當(dāng)讀取時,也是這樣操作,寫讀取4字節(jié)創(chuàng)建出一個32-bit的字符串長度值,然后再根據(jù)創(chuàng)建出的長度值讀取出相應(yīng)個數(shù)的字符,包括'\0'。
版本
QDataStream的二進(jìn)制格式從Qt1.0就開始形成了,很有可能在將來繼續(xù)進(jìn)化已反應(yīng)Qt的變化。當(dāng)操作復(fù)雜數(shù)據(jù)類型時,我們就要確保讀取和寫入時的QDataStream版本是一樣的。如果你需要向前和向后兼容,可以在代碼中使用硬編碼指定流的版本號:
stream.setVersion(QDataStream::Qt_4_0);
如果你正在創(chuàng)建一種新的二進(jìn)制數(shù)據(jù)格式,比如作為你的應(yīng)用程序創(chuàng)建的文件的格式,你可以使用QDataStream以一種可移植的格式去寫入這些數(shù)據(jù)。典型情況下,你可能會在文件頭寫入一個簡短的幻數(shù)字符串和一個版本數(shù)字,來用于將來擴(kuò)展。例如:
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;
那么,我們就可以以下面這種方式來讀取:
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ù)時指定一個字節(jié)序。默認(rèn)情況下是big endian。除非特殊需求,我們一個建議保持這個設(shè)置的默認(rèn)值,不做修改。
讀寫原生二進(jìn)制數(shù)據(jù)
有時,我們希望直接從data stream里讀取原生的二進(jìn)制數(shù)據(jù)。此時,可以使用readRawData() 將數(shù)據(jù)讀入一個預(yù)先分配好的char*;同樣的數(shù)據(jù)也可以使用writeRawData() 函數(shù)寫入data stream。但要記住,使用這種方式的話,要由你自己進(jìn)行所有數(shù)據(jù)的編碼和解碼。于此類似的另外兩個函數(shù)是readBytes() 和 writeBytes()。這兩個函數(shù)與上面的Raw版本相比,區(qū)別主要是:readBytes() 先讀取一個quint32值,該值被當(dāng)做將要讀取的數(shù)據(jù)的長度,然后讀取相應(yīng)字節(jié)的數(shù)據(jù)到預(yù)先定義好的char*中;writeBytes() 也同理,先寫入一個quint32的數(shù)據(jù)長度值,緊接著寫入相應(yīng)數(shù)據(jù)。同樣,使用這兩個函數(shù),所以數(shù)據(jù)的編碼和解碼要有我們自己負(fù)責(zé)。注意,readBytes() 不需要我們事先分配好內(nèi)存, 而readRawData() 需要我們事先分配好內(nèi)存。
讀寫Qt集合類和其他Qt類
Qt的常見集合類也可以使用QDataStream進(jìn)行序列化,這包括QList,QLinkedList,QVector,QSet,QHash和QMap。不過,序列化這些類的流操作符不是這個類的成員函數(shù)而已。同理,讀寫Qt中的其他類型,比如QImage,也是可以的。
使用事務(wù)
當(dāng)在一個異步的設(shè)備上讀取數(shù)據(jù)時,數(shù)據(jù)塊可以在任意的時間點(diǎn)上到來。所以,為了應(yīng)對這種情況,QDataStream提供了一個事務(wù)機(jī)制來確保原子性的完成一系列的流操作符。例如,你可以在操作socket時,在相應(yīng)readyRead() 的槽函數(shù)中,使用事務(wù)來完成不完整的數(shù)據(jù)讀取。
in.startTransaction(); QString str; qint32 a; in >> str >> a; // try to read packet atomically if (!in.commitTransaction()) return; // wait for more data
如果沒有完整的數(shù)據(jù)包到來,commitTransaction() 會返回false,并將stream重置為初始狀態(tài),然后,等待更多數(shù)據(jù)的到來。
下面給出一個簡單的測試程序:
#include <QCoreApplication> #include <QDebug> #include <QDataStream> #include <QFile> #include <QVector> #include <QMap> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); //write QFile file("test.dat"); if (!file.open(QIODevice::ReadWrite)) { qDebug() << "open file failed"; return 0; } QDataStream ds(&file); const char *wstr = "hello-world"; quint32 wi = 1234; double wd = 1.1; float wf = 2.2f; QVector<int> wvector; wvector.push_back(1); wvector.push_back(2); wvector.push_back(3); QMap<int,int> wmap; wmap.insert(4, 4); wmap.insert(5, 5); wmap.insert(6, 6); ds << wstr; ds << wi; ds << wd; ds << wf; ds << wvector; ds << wmap; ds.writeBytes("file end ", qstrlen("file end ")); ds.writeRawData("really end", qstrlen("really end")); //read file.seek(0); char *rstr; quint32 ri; double rd; float rf; QVector<int> rvector; QMap<int, int> rmap; char *rbytes; uint len; char *rraw = new char[100]{0}; int rlen; ds >> rstr; ds >> ri; ds >> rd; ds >> rf; ds >> rvector; ds >> rmap; ds.readBytes(rbytes, len); ds.readRawData(rraw, rlen); qDebug() << rstr; qDebug() << ri; qDebug() << rd; qDebug() << rf; qDebug() << rvector; qDebug() << rmap; qDebug() << rbytes; qDebug() << rraw; return a.exec(); }
到此這篇關(guān)于QT中QDataStream二進(jìn)制數(shù)據(jù)讀寫的實現(xiàn)的文章就介紹到這了,更多相關(guān)QT QDataStream二進(jìn)制數(shù)據(jù)讀寫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
常用的C++標(biāo)準(zhǔn)庫頭文件小結(jié)
C++標(biāo)準(zhǔn)庫定義了一系列函數(shù)、宏和對象,以實現(xiàn)跨團(tuán)隊、跨平臺的高效且具有卓越性能的標(biāo)準(zhǔn)化 C++ 代碼, 本文介紹常用的C++標(biāo)準(zhǔn)庫頭文件,需要的朋友可以參考下2023-11-11matlab?GUI指紋識別門禁系統(tǒng)介紹及源碼實現(xiàn)
這篇文章主要為大家介紹了matlab?GUI指紋識別門禁系統(tǒng)的介紹及源碼實現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02C++結(jié)構(gòu)體數(shù)組詳細(xì)解析
定義結(jié)構(gòu)體數(shù)組和定義結(jié)構(gòu)體變量類似,定義結(jié)構(gòu)體數(shù)組時只需聲明其為數(shù)組即可2013-10-10Dev C++編譯時運(yùn)行報錯source file not compile問題
這篇文章主要介紹了Dev C++編譯時運(yùn)行報錯source file not compile問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01