C# 入門指南

單元 19 - 解碼

~~學習進度表~~

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

Tbana kw dm wvuur.
×                 

012345678910111213141516171819202122232425
abcdefghijklmnopqrstwuvxyz

所謂的解碼也就是將編碼 (encoding) 過的小寫英文字母回覆成原來的英文小寫字母,由上圖可以看出,我們儲存表格所用的字串,恰巧依索引值 (index) 可推回原來的英文小寫字母,這是說,索引值 0'q' ,所以是將原本的 'a' 變成 'q' ,因此,解碼就是依索引值重新加上 diff 即可。

實際上我們需要用到巢狀迴圈 (nested loop) ,也就是在迴圈 (loop) 中有其他的迴圈,對單一英文句子而言,我們需要一個迴圈判斷每個字元是否為英文小寫字母,若是英文小寫字母,我們就需要另一個迴圈找出對應的索引值。由這樣的概念設計的解碼方法 ToDecode() ,如下

// 解碼方法
public string ToDecode(string s)
{
    // 建立暫存字串
    string result = "";

    // 第一層迴圈逐一取得字元
    for (int i = 0; i < s.Length; i++)
    {
        // 判斷是否為英文小寫字母
        if (s[i] >= 97 && s[i] <= 122)
        {
            // 第二層迴圈進行解碼轉換
            for (int j = 0; j < 26; j++)
            {
                // 找到字元在密碼表中的索引值
                if (s[i] == code[j])
                {
                    result += Convert.ToChar(j + 97);
                    break;
                }
            }
        }
        // 不是英文小寫字母,直接將字元附加到 result 之後
        else 
        {
            result += s[i];
        }
    }

    // 回傳結果
    return result;
}

巢狀迴圈是迴圈中包含另一個迴圈,由於我們利用縮排的方式編輯程式碼,看起來內層迴圈像是凹陷進去的巢,故稱之為巢狀迴圈。

ToDecode()ToEncode() 相似,同樣需要一個字串 (string) 當參數 (parameter) ,結果也回傳一個字串。

解碼轉換由巢狀迴圈的部份來進行

// 第一層迴圈逐一取得字元
for (int i = 0; i < s.Length; i++)
{
    // 判斷是否為英文小寫字母
    if (s[i] >= 97 && s[i] <= 122)
    {
        // 第二層迴圈進行解碼轉換
        for (int j = 0; j < 26; j++)
        {
            // 找到字元在密碼表中的索引值
            if (s[i] == code[j])
            {
                result += Convert.ToChar(j + 97);
                break;
            }
        }
    }
    // 不是英文小寫字母,直接將字元附加到 result 之後
    else 
    {
        result += s[i];
    }
}

第一層迴圈,也就是外層迴圈會依序取得英文句子的每個字元,然後判斷是否為英文小寫字母,如果該字元是英文小寫字母,就會啟動第二層迴圈找出表格中對應的索引值出來,相反地,如果該字元不是英文小寫字母,就會把該字元附加到 result 之後,控制變數 i 自動遞增,然後判斷下一個字元。

注意,這個巢狀迴圈用了兩個 for 、兩個 if ,依順序 forifforif ,這是我們打算讓程式執行的順序,若是沒有依照這樣的順序,程式可能會跑出無法預期的結果。

Encrypt 類別 (class) 到這邊大致已開發完成,完整程式如下

using System;

namespace EncryptDemo07
{
    class EncryptDemo07
    {
        // 宣告密碼表欄位
        public string code;

        // 建構子,呼叫建立密碼表的 SetCode() 方法
        public EncryptDemo07()
        {
            SetCode();
        }

        // 建立密碼表
        public void SetCode()
        {
            // 宣告變數
            int a, b, x, y, m;
            char c = 'a';
            // a 、 b 初始化
            Random r = new Random();
            a = 0;
            b = 0;
            // a 不能是 0 或偶數
            while (a % 2 == 0)
            {
                a = r.Next(1, 10);
                b = r.Next(0, 10);
            }
            code = "";
            // 建立密碼表的迴圈
            for (int i = 0; i < 26; i++)
            {
                x = c;
                y = x * a + b;
                m = y % 26;
                code += Convert.ToChar(m + 97);
                c++;
            }
        }

        // 編碼方法
        public string ToEncode(string s)
        {
            // 建立暫存字串
            string result = "";

            char c;
            int m;
            // 逐一取得字元進行處理
            for (int i = 0; i < s.Length; i++)
            {
                // 判斷是否為英文小寫字母
                if (s[i] >= 97 && s[i] <= 122)
                {
                    c = s[i];
                    m = c - 97;
                    result += code[m];
                }
                // 不是英文小寫字母,直接將字元附加到 result 之後
                else
                {
                    result += s[i];
                }
            }

            // 回傳結果
            return result;
        }

        // 解碼方法
        public string ToDecode(string s)
        {
            // 建立暫存字串
            string result = "";

            // 第一層迴圈逐一取得字元
            for (int i = 0; i < s.Length; i++)
            {
                // 判斷是否為英文小寫字母
                if (s[i] >= 97 && s[i] <= 122)
                {
                    // 第二層迴圈進行解碼轉換
                    for (int j = 0; j < 26; j++)
                    {
                        // 找到字元在密碼表中的索引值
                        if (s[i] == code[j])
                        {
                            result += Convert.ToChar(j + 97);
                            break;
                        }
                    }
                }
                // 不是英文小寫字母,直接將字元附加到 result 之後
                else 
                {
                    result += s[i];
                }
            }

            // 回傳結果
            return result;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // 建立密碼物件
            EncryptDemo07 e = new EncryptDemo07();
            // 印出密碼表
            Console.WriteLine(e.code);
            // 建立測試字串
            string s1 = "There is no spoon.";
            // 印出測試字串
            Console.WriteLine(s1);
            // 進行編碼
            string s2 = e.ToEncode(s1);
            // 印出編碼結果
            Console.WriteLine(s2);
            // 進行解碼
            string s3 = e.ToDecode(s2);
            // 印出解碼結果
            Console.WriteLine(s3);
        }
    }
}

//《程式語言教學誌》的範例程式
// http://kaiching.org/
// 專案:EncryptDemo07
// 檔名:Program.cs
// 功能:示範利用 C# 設計 Encrypt 類別
// 作者:張凱慶

執行部分加入了解碼字串

// 進行解碼
string s3 = e.ToDecode(s2);
// 印出解碼結果
Console.WriteLine(s3);

執行結果如下

C:\EncryptDemo07> dotnet run
venwfoxgpyhqzirajsbktcludm
There is no spoon.
Tgfsf pb ir barri.
There is no spoon.
C:\EncryptDemo07>

編碼與解碼的結果都正確無誤, Encrypt 類別的發展已經大體完備,下個單元繼續討論 C# 的程式結構。

相關教學影片

上一頁 單元 18 - 編碼
回 C# 入門指南首頁
下一頁 單元 20 - 程式結構與重構
回 C# 教材首頁
回程式語言教材首頁
中英文術語對照
類別class
解碼decoding
編碼encoding
索引值index
迴圈loop
巢狀迴圈nested loop
參數parameter
字串string
參考資料
1. C# 程式設計手冊 | Microsoft Docs - 類別
重點整理
1. 解碼與編碼用到相同的轉換表格,因為索引值 0 等於 'a' ,索引值 1 等於 'b' ,餘下可類推。
2. 巢狀迴圈是指迴圈中有其他迴圈,因為用縮排的方式編排程式碼,凹陷下去的地方看起來像鳥巢,故有此名。
3. 解碼利用巢狀迴圈進行,第一層迴圈逐一取得每一個字元,第二層迴圈尋找該字元在密碼表中的索引值。
問題與討論
1. 為什麼編碼跟解碼可以用一樣的轉換表格?
2. 可以不用巢狀迴圈來解碼嗎?
3. 巢狀迴圈可以用一樣名稱的控制變數嗎?
練習
1. 承接上一個單元的猜數字遊戲,將新專案寫在 Exercise1901 中,把答案 answerShuffle() 攪亂順序,然後用 Substring(0, 4) 取前四個數字當答案。
2. 承上題,將新專案寫在 Exercise1901 中,修正第一位數可能為 0 的情況。