編碼 (encoding) 需要用到轉換表格,我們利用陣列 (array) 儲存這個表格,簡單說,就是利用 Unicode 排列順序,相對到表格的對應關係
上圖是用了如下的表格
code = {'q', 'z', 'i', 'r', 'a', | |
'j', 's', 'b', 'k', 't', | |
'c', 'l', 'u', 'd', 'm', | |
'v', 'e', 'n', 'w', 'f', | |
'o', 'x', 'g', 'p', 'y', | |
'h'} |
我們先來想一想程式如何完成編碼工作,假設是對以下的字串 (string) 進行編碼
"There is no spoon." |
首先, 'T' 不是英文小寫字母,因此跳過,然後 'h' 、 'e' 、 'r' 、 'e' 都是英文小寫字母,對照表格,需要轉換為 'b' 、 'a' 、 'n' 、 'a' ,接下來遇到一個空格字元 ' ' ,也跳過,然後 'i' 、 's' 也都是英文小寫字母,需要轉換為 'k' 、 'w' ,餘下類推。
所以需要利用一個迴圈 (loop) 進行上述編碼工作,逐一檢查字串中的每一個字元 (character) ,若是屬於英文小寫字母的編碼範圍就是 Unicode 編碼 97 到 122 之間,我們先將該字元轉換為整數,然後減掉 97 就會是表格字串中對應字元索引值。
這是說,第 0 個字元(索引值為 0 ) 'T' 不在英文小寫字母編碼的範圍,因此程式不會處理,然後到第 1 個字元 'h' ,這是英文小寫字母編碼為 104 ,減去 97 之後為 7 ,對應到上面的表格會是 'b' ,因此得到的新字串第 1 個新字元就是 'b' ,餘下會一直進行重複的工作到字串結束為止。
因此,我們對 toEncode() 的設計如下
036 | // 編碼的方法 |
037 | public String toEncode(String s) { |
038 | char[] cs = s.toCharArray(); |
039 | int i, ci, d, m; |
040 | char r; |
041 | String rs = ""; |
042 | Character cc; |
043 | |
044 | for (i = 0; i < cs.length; i++) { |
045 | if (cs[i] >= 97 && cs[i] <= 122) { |
046 | ci = cs[i]; |
047 | m = ci - 97; |
048 | cs[i] = code[m]; |
049 | } |
050 | } |
051 | |
052 | for (i = 0; i < cs.length; i++) { |
053 | cc = new Character(cs[i]); |
054 | rs = rs.concat(cc.toString()); |
055 | } |
056 | |
057 | return rs; |
058 | } |
toEncode() 接收一個字串 s 當參數 (parameter) ,也回傳一個新字串 rs , s 就是要編碼的字串,而 rs 則是編碼過的字串。由於字串被設計成不可變的 (immutable) ,因此無法直接修改參數 s ,而要用一個新的字串變數。
由於接收的是字串,因此要先把字串轉換成字元陣列
038 | char[] cs = s.toCharArray(); |
進行編碼轉換的迴圈
044 | for (i = 0; i < cs.length; i++) { |
045 | if (cs[i] >= 97 && cs[i] <= 122) { |
046 | ci = cs[i]; |
047 | m = ci - 97; |
048 | cs[i] = code[m]; |
049 | } |
050 | } |
逐一判斷字元陣列中的字元,若該字元屬於英文小寫字母就進行編碼轉換。字元陣列的屬性 (field) length() 記錄陣列中的元素總數,迴圈的控制變數 i 最大不超過 length ,因為索引從 0 開始,最後一個元素的索引為 length 。
找到密碼表中要轉換的字元後,這裡直接將字元指派到本來的字元陣列中
048 | cs[i] = code[m]; |
最後再把字元陣列中的所有元素合併成字串
052 | for (i = 0; i < cs.length; i++) { |
053 | cc = new Character(cs[i]); |
054 | rs = rs.concat(cc.toString()); |
055 | } |
完整的實作檔案可以參考「範例程式碼」的 Encrypt.java 。
解碼 (decoding) 需要用到與編碼相同的轉換表格,也就是要將編碼過的小寫英文字母回覆成原來的英文小寫字母,我們儲存密碼表所用的陣列,恰巧依索引值 (index) 可推回原來的英文小寫字母,這是說,索引值 0 為 'q' ,所以是將原本的 'a' 變成 'q' ,因此,解碼就是依索引值重新加上 diff 即可。
實際上我們需要用到巢狀迴圈 (nested loop) ,也就是在迴圈中有其他的迴圈,對單一英文句子而言,我們需要一個迴圈判斷每個字元是否為英文小寫字母,若是英文小寫字母,我們就需要另一個迴圈找出對應的索引值。由這樣的概念設計的解碼方法 toDecode() ,如下
060 | // 解碼的方法 |
061 | public String toDecode(String s) { |
062 | char[] cs = s.toCharArray(); |
063 | int i, j; |
064 | char r; |
065 | String rs = ""; |
066 | Character cc; |
067 | |
068 | for (i = 0; i < cs.length; i++) { |
069 | if (cs[i] >= 97 && cs[i] <= 122) { |
070 | for (j = 0; j <= code.length; j++) { |
071 | if (cs[i] == code[j]) { |
072 | cs[i] = (char) (j + 97); |
073 | break; |
074 | } |
075 | } |
076 | } |
077 | } |
078 | |
079 | for (i = 0; i < cs.length; i++) { |
080 | cc = new Character(cs[i]); |
081 | rs = rs.concat(cc.toString()); |
082 | } |
083 | |
084 | return rs; |
085 | } |
巢狀迴圈是迴圈中包含另一個迴圈,由於我們利用縮排的方式編輯程式碼,看起來內層迴圈像是凹陷進去的巢,故稱之為巢狀迴圈。
toDecode() 與 toEncode() 相似,同樣需要一個字串當參數,結果也回傳一個字串。
解碼轉換由巢狀迴圈的部份來進行
068 | for (i = 0; i < cs.length; i++) { |
069 | if (cs[i] >= 97 && cs[i] <= 122) { |
070 | for (j = 0; j <= code.length; j++) { |
071 | if (cs[i] == code[j]) { |
072 | cs[i] = (char) (j + 97); |
073 | break; |
074 | } |
075 | } |
076 | } |
077 | } |
第一層迴圈,也就是外層迴圈會依序取得英文句子的每個字元,然後判斷是否為英文小寫字母,如果該字元是英文小寫字母,就會啟動另一個迴圈找出表格中對應的索引值出來,相反地,如果該字元不是英文小寫字母,控制變數 i 就會自動遞增,然後判斷下一個字元。
注意,這個巢狀迴圈用了兩個 for 、兩個 if ,依順序 for 、 if 、 for 、 if ,這是我們打算讓程式執行的順序,若是沒有依照這樣的順序,程式可能會跑出無法預期的結果。
編譯後執行,結果如下
編碼與解碼的結果都正確無誤, Encrypt 類別的發展已經大體完備,正式進入 GUI 開發之前,我們先來認識一下 Java API 囉!
中英文術語對照
編碼 | encoding |
陣列 | array |
字串 | string |
迴圈 | loop |
字元 | character |
參數 | parameter |
不可變的 | immutable |
屬性 | field |
解碼 | decoding |
索引值 | index |
巢狀迴圈 | nested loop |
重點整理
- Unicode 的英文字母是按照字母順序編排的。
- 利用字元陣列處理英文字串,要先把字串轉換成字元陣列。
- 編碼工作針對英文小寫字母,因此要先判斷該字元是否為英文小寫字母,若是英文小寫字母才進行編碼。
- 利用字串的 concat() 方法,可將另一個字串連結到原字串的最後面。
- 字串雖是不可變的資料型態,變數卻可以改變所指向的參考。
- 解碼工作也就是把編碼的過程顛倒過來,另外用內層迴圈找出表格中對應的索引值。
- 巢狀迴圈是迴圈中包含另一個迴圈,通常利用程式碼縮排來表示。
問題與討論
- 編碼可以直接用字串處理嗎?如果直接用字串處理的話,那該如何設計呢?
- 我們連接連結字串的方法,是用不斷的將新字串重新指派到原來的變數,這樣會不會很浪費記憶體,有其他方式可以改進嗎?
- 解碼工作一定得用巢狀迴圈嗎?有沒有簡單點的寫法?
- 可以直接對字串解碼嗎?如果直接對字串解碼,那該如何設計呢?
the end
沒有留言:
張貼留言