在上一章中,咱們介紹了有關QFile
和QFileInfo
兩個類的使用。咱們提到,QIODevice
提供了read()
、readLine()
等基本的操做。同時,Qt 還提供了更高一級的操做:用於二進制的流QDataStream
和用於文本流的QTextStream
。本節,咱們將講解有關QDataStream
的使用以及一些技巧。下一章則是QTextStream
的相關內容。網絡
QDataStream
提供了基於QIODevice
的二進制數據的序列化。數據流是一種二進制流,這種流徹底不依賴於 底層操做系統、CPU 或者字節順序(大端或小端)。例如,在安裝了 Windows 平臺的 PC 上面寫入的一個數據流,能夠不通過任何處理,直接拿到運行了 Solaris 的 SPARC 機器上讀取。因爲數據流就是二進制流,所以咱們也能夠直接讀寫沒有編碼的二進制數據,例如圖像、視頻、音頻等。函數
QDataStream
既可以存取 C++ 基本類型,如 int、char、short 等,也能夠存取複雜的數據類型,例如自定義的類。實際上,QDataStream
對於類的存儲,是將複雜的類分割爲不少基本單元實現的。性能
結合QIODevice
,QDataStream
能夠很方便地對文件、網絡套接字等進行讀寫操做。咱們從代碼開始看起:ui
QFile file("file.dat"); file.open(QIODevice::WriteOnly); QDataStream out(&file); out << QString("the answer is"); out << (qint32)42;
在這段代碼中,咱們首先打開一個名爲 file.dat 的文件(注意,咱們爲簡單起見,並無檢查文件打開是否成功,這在正式程序中是不容許的)。而後,咱們將剛剛建立的file
對象的指針傳遞給一個QDataStream
實例out
。相似於std::cout
標準輸出流,QDataStream
也重載了輸出重定向<<
運算符。後面的代碼就很簡單了:將「the answer is」和數字 42 輸出到數據流(若是你不明白這句話的意思,這但是宇宙終極問題的答案 ;-P 請自行搜索《銀河系漫遊指南》)。因爲咱們的 out 對象創建在file
之上,所以至關於將宇宙終極問題的答案寫入file
。編碼
須要指出一點:最好使用 Qt 整型來進行讀寫,好比程序中的qint32
。這保證了在任意平臺和任意編譯器都可以有相同的行爲。操作系統
咱們經過一個例子來看看 Qt 是如何存儲數據的。例如char *
字符串,在存儲時,會首先存儲該字符串包括 \0 結束符的長度(32位整型),而後是字符串的內容以及結束符 \0。在讀取時,先以 32 位整型讀出整個的長度,而後按照這個長度取出整個字符串的內容。指針
可是,若是你直接運行這段代碼,你會獲得一個空白的 file.dat,並無寫入任何數據。這是由於咱們的file
沒有正常關閉。爲性能起見,數據只有在文件關閉時纔會真正寫入。所以,咱們必須在最後添加一行代碼:rest
file.close(); // 若是不想關閉文件,可使用 file.flush();
從新運行一下程序,你就獲得宇宙終極問題的答案了。code
咱們已經得到宇宙終極問題的答案了,下面,咱們要將這個答案讀取出來:視頻
QFile file("file.dat"); file.open(QIODevice::ReadOnly); QDataStream in(&file); QString str; qint32 a; in >> str >> a;
這段代碼沒什麼好說的。惟一須要注意的是,你必須按照寫入的順序,將數據讀取出來。也就是說,程序數據寫入的順序必須預先定義好。在這個例子中,我 們首先寫入字符串,而後寫入數字,那麼就首先讀出來的就是字符串,而後纔是數字。順序顛倒的話,程序行爲是不肯定的,嚴重時會直接形成程序崩潰。
因爲二進制流是純粹的字節數據,帶來的問題是,若是程序不一樣版本之間按照不一樣的方式讀取(前面說過,Qt 保證讀寫內容的一致,可是並不能保證不一樣 Qt 版本之間的一致),數據就會出現錯誤。所以,咱們必須提供一種機制來確保不一樣版本之間的一致性。一般,咱們會使用以下的代碼寫入:
QFile file("file.dat"); file.open(QIODevice::WriteOnly); QDataStream out(&file); // 寫入魔術數字和版本 out << (quint32)0xA0B0C0D0; out << (qint32)123; out.setVersion(QDataStream::Qt_4_0); // 寫入數據 out << lots_of_interesting_data;
這裏,咱們增長了兩行代碼:
out << (quint32)0xA0B0C0D0;
用於寫入魔術數字。所謂魔術數字,是二進制輸出中常用的一種技術。二進制格式是人不可讀的,而且一般具備相同的後綴名(好比 dat 之類),所以咱們沒有辦法區分兩個二進制文件哪一個是合法的。因此,咱們定義的二進制格式一般具備一個魔術數字,用於標識文件的合法性。在本例中,咱們在文 件最開始寫入 0xA0B0C0D0,在讀取的時候首先檢查這個數字是否是 0xA0B0C0D0。若是不是的話,說明這個文件不是可識別格式,所以根本不須要去繼續讀取。通常二進制文件都會有這麼一個魔術數字,例如 Java 的 class 文件的魔術數字就是 0xCAFEBABE,使用二進制查看器就能夠查看。魔術數字是一個 32 位的無符號整型,所以咱們使用quint32
來獲得一個平臺無關的 32 位無符號整型。
接下來一行,
out << (qint32)123;
是標識文件的版本。咱們用魔術數字標識文件的類型,從而判斷文件是否是合法的。可是,文件的不一樣版本之間也可能存在差別:咱們可能在初版保存整型,第二版可能保存字符串。爲了標識不一樣的版本,咱們只能將版本寫入文件。好比,如今咱們的版本是 123。
下面一行仍是有關版本的:
out.setVersion(QDataStream::Qt_4_0);
上面一句是文件的版本號,可是,Qt 不一樣版本之間的讀取方式可能也不同。這樣,咱們就得指定 Qt 按照哪一個版本去讀。這裏,咱們指定以 Qt 4.0 格式去讀取內容。
當咱們這樣寫入文件以後,咱們在讀取的時候就須要增長一系列的判斷:
QFile file("file.dat"); file.open(QIODevice::ReadOnly); QDataStream in(&file); // 檢查魔術數字 quint32 magic; in >> magic; if (magic != 0xA0B0C0D0) { return BAD_FILE_FORMAT; } // 檢查版本 qint32 version; in >> version; if (version < 100) { return BAD_FILE_TOO_OLD; } if (version > 123) { return BAD_FILE_TOO_NEW; } if (version <= 110) { in.setVersion(QDataStream::Qt_3_2); } else { in.setVersion(QDataStream::Qt_4_0); } // 讀取數據 in >> lots_of_interesting_data; if (version >= 120) { in >> data_new_in_version_1_2; } in >> other_interesting_data;
這段代碼就是按照前面的解釋進行的。首先讀取魔術數字,檢查文件是否合法。若是合法,讀取文件版本:小於 100 或者大於 123 都是不支持的。若是在支持的版本範圍內(100 <= version <= 123),則當是小於等於 110 的時候,按照Qt_3_2
的格式讀取,不然按照Qt_4_0
的格式讀取。當設置完這些參數以後,開始讀取數據。
至此,咱們介紹了有關QDataStream
的相關內容。那麼,既然QIODevice
提供了read()
、readLine()
之類的函數,爲何還要有QDataStream
呢?QDataStream
同QIODevice
有什麼區別?區別在於,QDataStream
提供流的形式,性能上通常比直接調用原始 API 更好一些。咱們經過下面一段代碼看看什麼是流的形式:
QFile file("file.dat"); file.open(QIODevice::ReadWrite); QDataStream stream(&file); QString str = "the answer is 42"; QString strout; stream << str; file.flush(); stream >> strout;
在這段代碼中,咱們首先向文件中寫入數據,緊接着把數據讀出來。有什麼問題嗎?運行以後你會發現,strout
實際是空的。爲何沒有讀取出來?咱們不是已經添加了file.flush();
語句嗎?緣由並不在於文件有沒有寫入,而是在於咱們使用的是「流」。所謂流,就像水流同樣,它的遊標會隨着輸出向後移動。當使用<<
操做符輸出以後,流的遊標已經到了最後,此時你再去讀,固然什麼也讀不到了。因此你須要在輸出以後從新把遊標設置爲 0 的位置纔可以繼續讀取。具體代碼片斷以下:
stream << str; stream.device()->seek(0); stream >> strout;