Morse Code

电影无间道中,陈永仁用摩斯密码向自己老大发送消息那一段真的是精彩,印象非常深刻。其实摩斯密码的本质上就是一种消息编码格式,也就是通信双方用规定好的格式进行交流,由于该编码方式二义性小,所以接受方基本可以准确无误的解析出发送方经过编码的信息。

摩斯密码

摩斯密码主要编码英文字母,数字和标点符号,摩斯密码与常用的二进制编码不同,它包含了五个元素, 点、划、点和划之间的停顿、每个词之间中等的停顿以及句子之间长的停顿。

Morse Code

因为摩斯密码的编码方法不止一种,但其基本原理都是相通的,所以这里我们使用国际电信联盟(International Telecommunication Union)制定的编码标准来书写代码。

下面使用一个 map 作为字典使用,用于记录英文字母、数字和标点符号到摩斯密码的映射关系,因为文章篇幅原因,这里选择省略对应关系。

1
2
3
4
5
6
7
8
9
10
11
var (
morseITU = map[string]string{
"a": ".-",

// ...

"0": "-----",

"=": "-...-",
}
)

编码

编码的过程就是利用字典将我们要映射的字符转为摩斯密码,并且注意每次转换都要附加一个 letterSep ,也就是字符间的分隔符,当遇到原本需要编码的字符串中的空格,则加上 wordSep ,即字分隔符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func EncodeITU(s string) string {
return Encode(s, morseITU, " ", "/")
}

func Encode(s string, alphabet map[string]string, letterSep string, wordSep string) string {
res := ""
for _, part := range s {
p := string(part)
if p == " " {
if wordSep != "" {
res += wordSep + letterSep
}
} else if morseITU[p] != "" {
res += morseITU[p] + letterSep
}
}
return strings.TrimSpace(res)
}

解码

解码是编码的逆过程,因为有字符分隔符(letterSep)的存在,所以我们先用 strings.Split(s, letterSep) 得到一组经过摩斯密码编码的字符数组,然后对于每个经过编码的摩斯密码,我们查询字典中是否有该编码存在,如果存在则与返回结果拼接,如果不存在则检查该元素是否为字分隔符,如果还不是的话则返回错误,最后全部成功返回解码后的结果。

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
func DecodeITU(s string) string {
return Decode(s, morseITU, " ", "/")
}

func Decode(s string, alphabet map[string]string, letterSep string, wordSep string) (string, error) {
res := ""
for _, part := range strings.Split(s, letterSep) {
found := false
for key, val := range alphabet {
if val == part {
res += key
found = true
break
}
}
if part == wordSep {
res += " "
found = true
}
if found == false {
return res, fmt.Errorf("unknown character " + part)
}
}
return res, nil
}
Pieces of Valuable Programming Knowledges