<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                #(36):二進制文件讀寫 在上一章中,我們介紹了有關`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`可以很方便地對文件、網絡套接字等進行讀寫操作。我們從代碼開始看起: ~~~ 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`沒有正常關閉。為性能起見,數據只有在文件關閉時才會真正寫入。因此,我們必須在最后添加一行代碼: ~~~ file.close(); // 如果不想關閉文件,可以使用 file.flush(); ~~~ 重新運行一下程序,你就得到宇宙終極問題的答案了。 我們已經獲得宇宙終極問題的答案了,下面,我們要將這個答案讀取出來: ~~~ 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; ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看