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