C++ 入門指南 4.01

單元 19 - 解碼

-unit19-

解碼 (decoding) 需要用到與編碼相同的轉換表格

Tbana kw dm wvuur.
×                 

012345678910111213141516171819202122232425
abcdefghijklmnopqrstwuvxyz

所謂的解碼也就是將編碼 (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 ,依順序 forifforif ,這是我們打算讓程式執行的順序,若是沒有依照這樣的順序,程式可能會跑出無法預期的結果。

這裡只增加一個 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_demo03-

編碼與解碼的結果都正確無誤, 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. 承上題, mapfind() 可用於尋找是否有指定的 key ,結果回傳迭代器,寫一個程式 exercise1902.cxx ,利用 find() 尋找 map 中是否有指定的 key ,然後以回傳的迭代器是否等於 end() 來判斷是否有找到。 參考程式碼
3. 承上題,利用指派運算子可以重新設定 map 的元素, at() 回傳指定 key 的 value ,寫一個程式 exercise1903.cxx ,先利用指派運算子重新設定某一 key 的 value ,再用 at() 印出這個 value 。 參考程式碼
4. 承上題, maperase() 可以移除掉指定的元素,參數必須是迭代器,寫一個程式 exercise1904.cxx ,用迴圈找到指定的迭代器,然後移除指定的 map 元素。 參考程式碼
5. 承上題, mapswap() 可以交換不同 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'參考程式碼

相關教學影片

上一頁 單元 18 - 編碼
回 C++ 入門指南 4.01 目錄
下一頁 單元 20 - 型態轉換問題
回 C++ 教材
回程式語言教材首頁