
我們要存檔,儲存的到底是什麼呢?物件 (object) 嗎?資料成員 (data member) 嗎?還有成員函數 (member function) 呢?

如果某次編碼結果不錯,我們往後想要繼續利用同一個 Encrypt 物件,這時候就需要把 Encrypt 物件儲存下來。想一想我們存檔應該儲存什麼?把整個 Encrypt 物件都儲存下來,還是只要儲存編碼用的 code_array 就可以了呢?
問題很簡單,就是存檔究竟要儲存什麼,儲存整個 Encrypt 物件也不是不行,不過這樣存檔載入都變得有點複雜,物件還得序列化 (serialization) 等等。想一想,如果情況改成遊戲程式的話,儲存的不外是遊戲狀態或進度的資料表,資料表中大概都是數字或字串 (string) ,因此實際儲存的也會是數字跟字串。
如果是要儲存數字或字串,解決問題的方式就更簡單了,因為數字或字串都是很常用的資料格式,程式庫針對很常用的資料格式都有直接套用的方式,因此我們可以直接儲存密碼表字串就可以了。
我們對 on_pushButton_save_clicked() 的實作如下
| 041 | // 按下 Save 按鈕的事件 |
| 042 | void EncryptWindow::on_pushButton_save_clicked() |
| 043 | { |
| 044 | // 先測試是否有按過 New 按鈕 |
| 045 | if (e != NULL) { |
| 046 | // 有按過 New 按鈕,建立檔名 encryptor 的 QFile 物件 |
| 047 | QFile file("encryptor"); |
| 048 | // 以寫入模式開啟檔案 |
| 049 | file.open(QIODevice::WriteOnly); |
| 050 | // 建立 QDataStream 物件讀取檔案串流 |
| 051 | QDataStream out(&file); |
| 052 | // 從 QDataStream 物件將密碼表輸出到檔案 |
| 053 | out << s2q(e->get_code_array()); |
| 054 | |
| 055 | // 最後在 label_display 顯示提示訊息 |
| 056 | ui->label_display->setText("Encrypt object is saved."); |
| 057 | } |
| 058 | else { |
| 059 | // 沒按過 New 按鈕,在 label_display 顯示提示訊息 |
| 060 | ui->label_display->setText("There is no Encrypt Object."); |
| 061 | } |
| 062 | } |
完整程式請參考 encryptwindow.cpp 。
這裡同樣做了個預防措施,如果 e 為 NULL ,就是使用者沒有按過 New 按鈕,因此沒有密碼表可以儲存,所以直接在 label_display 顯示提示訊息。
實際存檔是建立 QFile 型態 (type) 的物件 file ,並以檔名字串當建構函數 (constructor) 的參數 (parameter) ,接著設定開啟方式為寫入,也就是 QIODevice::WriteOnly
| 046 | // 有按過 New 按鈕,建立檔名 encryptor 的 QFile 物件 |
| 047 | QFile file("encryptor"); |
| 048 | // 以寫入模式開啟檔案 |
| 049 | file.open(QIODevice::WriteOnly); |
接著用 QDataStream 型態的物件 out 進行寫入,需要以 QFile 物件的指標當參數,最後用輸出運算子將密碼表寫到 out 內,也就是存到檔案 encryptor 中
| 050 | // 建立 QDataStream 物件讀取檔案串流 |
| 051 | QDataStream out(&file); |
| 052 | // 從 QDataStream 物件將密碼表輸出到檔案 |
| 053 | out << s2q(e->get_code_array()); |
這裡的變數 out 跟標準程式庫 iostream 中的 out 有一樣的名稱,兩者都表示輸出,前者輸出到檔案,後者則是輸出到標準輸出裝置。
Mac 系統裡 encryptor 檔案可能會儲存在編譯好的 encrypt_gui 執行檔套件內。
這樣就完成存檔了,倒是用了 QFile 與 QDataStream ,就要先 #include 進來
| 004 | #include <QFile> |
| 005 | #include <QDataStream> |
下面我們繼續看到載入 on_pushButton_load_clicked() 的實作
| 064 | // 按下 Load 按鈕的事件 |
| 065 | void EncryptWindow::on_pushButton_load_clicked() |
| 066 | { |
| 067 | // 建立檔名 encryptor 的 QFile 物件 |
| 068 | QFile file("encryptor"); |
| 069 | // 以唯讀模式開啟,先測試檔案存不存在 |
| 070 | if (file.open(QIODevice::ReadOnly)) { |
| 071 | // 檔案存在,建立 QDataStream 物件讀取檔案串流 |
| 072 | QDataStream in(&file); |
| 073 | // 建立一個 QString 字串暫存密碼表 |
| 074 | QString temp; |
| 075 | // 從 QDataStream 物件將密碼表輸出到 QString 字串 |
| 076 | in >> temp; |
| 077 | |
| 078 | // 如果使用者沒按過 New ,先新建成員變數 e |
| 079 | if (e == NULL) { |
| 080 | e = new Encrypt; |
| 081 | } |
| 082 | // 將密碼表寫入成員變數 e |
| 083 | e->set_code_array(q2s(temp)); |
| 084 | |
| 085 | // 最後在 label_display 顯示提示訊息 |
| 086 | ui->label_display->setText("Encrypt object is loaded."); |
| 087 | } |
| 088 | else { |
| 089 | // 檔案不存在,在 label_display 顯示提示訊息 |
| 090 | ui->label_display->setText("Encrypt object is not loaded."); |
| 091 | } |
| 092 | } |
跟存檔比較不一樣的是這裡先建立 QFile 檔案物件 file ,然後用唯獨模式 QIODevice::ReadOnly 開啟, open() 會傳布林值,若存在檔案就是 true
| 067 | // 建立檔名 encryptor 的 QFile 物件 |
| 068 | QFile file("encryptor"); |
| 069 | // 以唯讀模式開啟,先測試檔案存不存在 |
| 070 | if (file.open(QIODevice::ReadOnly)) { |
在其他程式語言中,有關檔案的部份可能要放到例外處理 (exception handling) 之中, C++ 也有例外處理的機制,倒是沒有強制限制要用例外處理檔案,因此直接測試檔案是否存在,如此可減少額外的處理時間。
接下來由 QDataStream 型態的 in 寫入資料到 QString 的 temp 中
| 071 | // 檔案存在,建立 QDataStream 物件讀取檔案串流 |
| 072 | QDataStream in(&file); |
| 073 | // 建立一個 QString 字串暫存密碼表 |
| 074 | QString temp; |
| 075 | // 從 QDataStream 物件將密碼表輸出到 QString 字串 |
| 076 | in >> temp; |
後續測試 e 是不是 NULL ,如果是 NULL 就新建 Encrypt ,不然可能會發生不能寫入的問題,最後就直接用 e 設定密碼表。
| 078 | // 如果使用者沒按過 New ,先新建成員變數 e |
| 079 | if (e == NULL) { |
| 080 | e = new Encrypt; |
| 081 | } |
| 082 | // 將密碼表寫入成員變數 e |
| 083 | e->set_code_array(q2s(temp)); |
咦?有參數版本的 set_code_array() 並無實作,留待練習來實作好了,下個單元我們繼續完成其他功能囉!
中英文術語對照
| 物件 | object |
| 資料成員 | data member |
| 成員函數 | member function |
| 序列化 | serialization |
| 字串 | string |
| 型態 | type |
| 建構函數 | constructor |
| 參數 | parameter |
| 例外處理 | exception handling |
重點整理
- 存檔機制採用儲存密碼表的方式,利用 QFile 及 QDataStream 將密碼表寫進 encryptor 中。
- 載入同樣利用 QFile 及 QDataStream ,將密碼表讀取到暫存的 QString 字串,然後再寫入密碼表。
問題與討論
- 比較儲存物件及儲存字串的優劣,列出兩者的優缺點。
- 什麼是例外處理?什麼情況下要做例外處理?
練習
- 實作 set_code_array(string) ,除了要在實作檔實作外,也要在標頭檔加入宣告。
- 承接上一個單元的 guess_game 專案,想一想要不要有存檔的機制。
the end
沒有留言:
張貼留言
0.留言請選擇註冊帳號, Google 或 OpenID 均可
1.歡迎留言交流,但不歡迎垃圾留言及廣告留言
2.文章相關問題歡迎提出,請減少情緒性留言
3.非文章相關內容,請到 G+ 社群或 FB 社團提出
4.問作業之留言會被直接刪除
5.莫忘網路禮節
6.可使用部份HTML標記,如 <b> 、 <i> 、 <a>
7.站長保留刪除留言的權力