C++ 入門指南 4.01
單元 19 - 解碼
解碼 (decoding) 需要用到與編碼相同的轉換表格
×
⇓
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | w | u | v | x | y | z |
所謂的解碼也就是將編碼 (encoding) 過的小寫英文字母回覆成原來的英文小寫字母,由上圖可以看出,我們儲存表格所用的陣列 (array) ,恰巧依索引值 (index) 可推回原來的英文小寫字母,這是說,索引值 0 為 'q' ,所以是將原本的 'a' 變成 'q' ,因此,解碼就是依索引值重新加上 diff 即可。
實際上我們需要用到巢狀迴圈 (nested loop) ,也就是在迴圈 (loop) 中有其他的迴圈,對單一英文句子而言,我們需要一個迴圈判斷每個字元是否為英文小寫字母,若是英文小寫字母,我們就需要另一個迴圈找出對應的索引值。由這樣的概念設計的解碼成員函數 (member function) ToDecode() ,如下
// 進行解碼工作的成員函數 string Encrypt::ToDecode(string s) { // 暫存解碼結果的字串 string r; int i, j; // 第一層迴圈逐一取得每一個字元 for (i = 0; i < s.size(); i++) { // 判斷該字元是否為英文小寫字母,若是英文小寫字母就進行解碼轉換 if (s.at(i) >= DIFF && s.at(i) < DIFF + N) { // 第二層迴圈尋找該字元在密碼表中的索引值 // 該索引值加上 DIFF 就可轉換回原本的字元 for (j = 0; j < N; j++) { if (s.at(i) == get_code_array().at(j)) { r += (char) j + DIFF; break; } } } else { r += s.at(i); } } // 結束回傳解碼過的字串 return r; }
巢狀迴圈是迴圈中包含另一個迴圈,由於我們利用縮排的方式編輯程式碼,看起來內層迴圈像是凹陷進去的巢,故稱之為巢狀迴圈。
ToDecode() 與 ToEncode() 相似,同樣需要一個字串 (string) 當參數 (parameter) ,結果也回傳一個字串。
解碼轉換由巢狀迴圈的部份來進行
// 第一層迴圈逐一取得每一個字元 for (i = 0; i < s.size(); i++) { // 判斷該字元是否為英文小寫字母,若是英文小寫字母就進行解碼轉換 if (s.at(i) >= DIFF && s.at(i) < DIFF + N) { // 第二層迴圈尋找該字元在密碼表中的索引值 // 該索引值加上 DIFF 就可轉換回原本的字元 for (j = 0; j < N; j++) { if (s.at(i) == get_code_array().at(j)) { r += (char) j + DIFF; break; } } } else { r += s.at(i); } }
第一層迴圈,也就是外層迴圈會依序取得英文句子的每個字元,然後判斷是否為英文小寫字母,如果該字元是英文小寫字母,就會啟動另一個迴圈找出表格中對應的索引值出來,相反地,如果該字元不是英文小寫字母,控制變數 i 就會自動遞增,然後判斷下一個字元。
注意,這個巢狀迴圈用了兩個 for 、兩個 if ,依順序 for 、 if 、 for 、 if ,這是我們打算讓程式執行的順序,若是沒有依照這樣的順序,程式可能會跑出無法預期的結果。
這裡只增加一個 ToDecode() ,完整的實作檔案可以參考「範例程式碼」的 encrypt.cxx 。
Encrypt 類別 (class) 到這邊大致已開發完成,我們用下面的 encrypt_demo.cxx 來測試囉
// 引入標準程式庫中的 iostream #include <iostream> // 使用 std 中的兩個名稱 using std::cout; // 標準輸出串流的物件 using std::endl; // 新行符號,等於 '\n' // 引入 Encrypt 類別的標頭檔 #include "encrypt.h" // 程式執行的 main() 函數 int main() { // 印出空白一行 cout << endl; // 建立 Encrypt 物件 Encrypt encryptor; // 印出密碼表字串 cout << encryptor.get_code_array() << endl << endl; // 印出準備編碼的字串 string d = "There is no spoon."; cout << d << endl; // 將字串編碼,然後印出編碼結果 string r1 = encryptor.ToEncode(d); cout << r1 << endl << endl; // 重新解碼碼,然後印出解碼結果 string r2 = encryptor.ToDecode(r1); cout << r2 << endl << endl; return 0; } /* 《程式語言教學誌》的範例程式 http://kaiching.org/ 檔名:encrypt_demo.cxx 功能:Encrypt 類別的標頭檔 作者:張凱慶 */
編譯 (compile) 執行,結果如下
編碼與解碼的結果都正確無誤, Encrypt 類別的發展已經大體完備,不過有個編譯器 (compiler) 不會特別提醒的小問題,我們需要稍微倒帶一下,重新檢視 set_code_array() 。
中英文術語對照 | |
---|---|
陣列 | array |
類別 | class |
編譯 | compile |
編譯器 | compiler |
解碼 | decoding |
編碼 | encoding |
索引值 | index |
迴圈 | loop |
成員函數 | member function |
巢狀迴圈 | nested loop |
參數 | parameter |
字串 | string |
重點整理 |
---|
1. 解碼與編碼用到相同的轉換表格,因為索引值 0 等於 'a' ,索引值 1 等於 'b' ,餘下可類推。 |
2. 巢狀迴圈是指迴圈中有其他迴圈,因為用縮排的方式編排程式碼,凹陷下去的地方看起來像鳥巢,故有此名。 |
3. 解碼利用巢狀迴圈進行,第一層迴圈逐一取得每一個字元,第二層迴圈尋找該字元在密碼表中的索引值。 |
問題與討論 |
---|
1. 為什麼編碼跟解碼可以用一樣的轉換表格? |
2. 可以不用巢狀迴圈來解碼嗎? |
3. 巢狀迴圈可以用一樣名稱的控制變數嗎? |
練習 |
---|
1. map 可以在迴圈中用迭代器處理其中資料,寫一個程式 exercise1901.cxx ,先用字面常數建立 map 變數,在迴圈中以 begin() 取得第一個元素,結束元素為 end() ,遞增運算子 ++ 可依次取得元素,然後用 first 印出 key , second 印出 value 。 參考程式碼 |
2. 承上題, map 的 find() 可用於尋找是否有指定的 key ,結果回傳迭代器,寫一個程式 exercise1902.cxx ,利用 find() 尋找 map 中是否有指定的 key ,然後以回傳的迭代器是否等於 end() 來判斷是否有找到。 參考程式碼 |
3. 承上題,利用指派運算子可以重新設定 map 的元素, at() 回傳指定 key 的 value ,寫一個程式 exercise1903.cxx ,先利用指派運算子重新設定某一 key 的 value ,再用 at() 印出這個 value 。 參考程式碼 |
4. 承上題, map 的 erase() 可以移除掉指定的元素,參數必須是迭代器,寫一個程式 exercise1904.cxx ,用迴圈找到指定的迭代器,然後移除指定的 map 元素。 參考程式碼 |
5. 承上題, map 的 swap() 可以交換不同 map 中的元素,寫一個程式 exercise1905.cxx ,交換兩個 map 元素,然後印出交換後的結果。 參考程式碼 |
6. C++ 可以套用 C 語言標準程式庫中的程式,例如可以用輸出到命令列上的 printf() 函數,寫一個程式 exercise1906.cxx ,在命令列上印出 "Hello World!!\n" ,結尾的 "\n" 是斷行符號。 參考程式碼 |
7. 承上題, printf() 可以用格式化字串進行輸出,這是在輸出字串中加入指定型態的轉換符號,然後用變數代入,寫一個程式 exercise1907.cxx ,在輸出字串中用表示整數的 %d 及浮點數的 %f ,分別印出整數及浮點數。 參考程式碼 |
8. 承上題,格式化字串的特點是可以精準控制命令列輸出的空白字元,例如 %7d 是含整數有 7 位,像是整數有 3 位,整數的左側就會用 4 位空白字元去填補, %5.2f 的道理也是一樣, 5 表示小數點前的位數, 2 表示小數點之後的位數,寫一個程式 exercise1908.cxx ,在轉換符號加入數字,使顯示數字的最後一位得以對齊。 參考程式碼 |
9. 承上題,字元的轉換符號為 %c ,字串為 %s ,寫一個程式 exercise1909.cxx ,印出對齊的格式化字元及字串。 參考程式碼 |
10. 承上題,十六進位整數轉換符號為 %#x ,此外對齊空格數也可以用參數設定,寫一個程式 exercise1910.cxx ,印出一個十六進位數字,然後用參數的方式印出另一個對齊的字元 'a' 。 參考程式碼 |
相關教學影片