Python 入門指南 V2.00 - 單元 19 - 解碼




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



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


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


001# 使用 randint()
002import random
003
004# 定義 Encrypt 類別 
005class Encrypt:
006   def __init__(self):
007      self.setcode()
008
009   def setcode(self):
010      # 取得 a 、 b 值
011      a = 0
012      b = 0
013      while a % 2 == 0: 
014         a = random.randint(0, 9)
015         b = random.randint(0, 9) 
016
017      # 利用公式建立密碼表
018      self.code = ""
019      c = "a"
020      i = 0
021      while i < 26:
022         x = c
023         y = ord(x) * a + b
024         m = y % 26
025         self.code += chr(m + 97)
026         c = chr(ord(c) + 1)
027         i += 1
028
029   def getcode(self):
030      return self.code
031
032   # 編碼的方法
033   def toEncode(self, str):
034      # 暫存編碼結果的字串
035      result = ""
036
037      # 利用迴圈走完參數字串的所有字元
038      for c in str:
039         # 判斷該字元是否為英文小寫字母
040         # 若是英文小寫字母就進行編碼轉換
041         c1 = ord(c) >= 97
042         c2 = ord(c) <= 122
043         if c1 and c2:
044            m = ord(c) - 97
045            result += self.code[m]
046         else:
047            result += c
048
049      # 結束回傳編碼過的字串
050      return result
051
052   # 解碼的方法 
053   def toDecode(self, str):
054      # 暫存解碼結果的字串
055      result = ""
056
057      i = 0
058      # 第一層迴圈逐一取得每一個字元
059      while i < len(str):
060         # 判斷該字元是否為英文小寫字母
061         # 若是英文小寫字母就進行解碼轉換
062         i1 = ord(str[i]) >= 97
063         i2 = ord(str[i]) <= 122
064         if i1 and i2:
065            j = 0
066            # 第二層迴圈尋找該字元在密碼表中
067            # 的索引值,加上 DIFF 就可轉換
068            # 回原本的字元
069            while j < len(self.code):
070               if str[i] == self.code[j]:
071                  result += chr(j + 97)
072               j += 1
073         else:
074            result += str[i]
075
076         i += 1
077
078      # 結束回傳解碼過的字串
079      return result
080
081# 測試部分
082if __name__ == '__main__':
083   e = Encrypt()
084   print()
085   print(e.getcode())
086   s1 = "There is no spoon."
087   print("Input : " + s1)
088   s2 = e.toEncode(s1)
089   print("Encode: " + s2)
090   s3 = e.toDecode(s2)
091   print("Decode: " + s3)
092   print()
093 
094# 檔名: encrypt07.py 
095# 作者: Kaiching Chang 
096# 時間: July, 2014

toDecode()toEncode() 相似,同樣需要一個字串 (string) 當參數 (parameter) ,結果也回傳一個字串,解碼轉換則由 toDecode() 的巢狀迴圈部份來進行


057i = 0
058# 第一層迴圈逐一取得每一個字元
059while i < len(str):
060   # 判斷該字元是否為英文小寫字母
061   # 若是英文小寫字母就進行解碼轉換
062   i1 = ord(str[i]) >= 97
063   i2 = ord(str[i]) <= 122
064   if i1 and i2:
065      j = 0
066      # 第二層迴圈尋找該字元在密碼表中
067      # 的索引值,加上 DIFF 就可轉換
068      # 回原本的字元
069      while j < len(self.code):
070         if str[i] == self.code[j]:
071            result += chr(j + 97)
072         j += 1
073   else:
074      result += str[i]
075
076   i += 1

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

第一層迴圈,也就是外層迴圈會依序取得英文句子的每個字元,然後判斷是否為英文小寫字母,如果該字元是英文小寫字母,就會啟動第二層迴圈找出表格中對應的索引值出來。控制變數 i 在最後會遞增,接著判斷下一個字元。


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


Encrypt 類別到這邊大致已開發完成,最後的測試部分也加入了解碼的相關程式碼


081# 測試部分
082if __name__ == '__main__':
083   e = Encrypt()
084   print()
085   print(e.getcode())
086   s1 = "There is no spoon."
087   print("Input : " + s1)
088   s2 = e.toEncode(s1)
089   print("Encode: " + s2)
090   s3 = e.toDecode(s2)
091   print("Decode: " + s3)
092   print()

執行結果如下



結果如預期,下面我們進入 GUI 之前先岔開討論一個問題,就是我們程式雖然順利跑出結果,可是一點也不 Python 說,因此我們要先來重構 (refactoring) 程式碼。


中英文術語對照


解碼decoding
編碼encoding
字串string
索引值index
巢狀迴圈nested loop
迴圈loop
方法method
字串string
參數parameter
重構refactoring

重點整理


  1. 解碼與編碼用到相同的轉換表格,因為索引值 0 等於 'a' ,索引值 1 等於 'b' ,餘下可類推。
  2. 巢狀迴圈是指迴圈中有其他迴圈,因為用縮排的方式編排程式碼,凹陷下去的地方看起來像鳥巢,故有此名。
  3. 解碼利用巢狀迴圈進行,第一層迴圈逐一取得每一個字元,第二層迴圈尋找該字元在密碼表中的索引值。


問題與討論


  1. 為什麼編碼跟解碼可以用一樣的轉換表格?
  2. 可以不用巢狀迴圈來解碼嗎?
  3. 巢狀迴圈可以用一樣名稱的控制變數嗎?

練習


  1. 承接上一個單元的猜數字遊戲,將新程式寫在 exercise1901.py 中,修正第一位數可能為 0 的情況。
  2. 承上題,將猜數字遊戲改用類別 GuessGame 設計,答案用 answer 屬性儲存,另外設置 timesab 等屬性,把設定以上屬性的程式內容放在 set_game() 方法裡。

the end

沒有留言: