Java MorseCoder - Java 語言實現的摩爾斯電碼編碼解碼html
代碼已經扔到 GitHub 上了,地址在 https://github.com/TakWolf/Java-MorseCoderjava
關於摩爾斯電碼
不詳細介紹了,維基百科上面介紹的更詳細。請參考: 維基百科 -> 摩爾斯電碼git
說幾個重要的概念。github
摩爾斯電碼由三種類型的信號組成,分別爲:短信號(滴)、長信號(嗒)和分隔符。三種信號一般習慣使用「.」、「-」、「/」表示。數組
摩爾斯電碼有一個密碼錶,用來映射密碼。密碼錶以下:(注意:字母都會轉換爲大寫,0 爲短信號,1 爲長信號。)app
字符
電碼
A
01B
1000C
1010D
100E
0F
0010G
110H
0000I
00J
0111K
101L
0100M
11N
10O
111P
0110Q
1101R
010S
000T
1U
001V
0001W
011X
1001Y
1011Z
1100
字符
電碼
0
111111
011112
001113
000114
000015
000006
100007
110008
111009
11110
字符
電碼
.
010101,
110011?
001100‘
011110!
101011/
10010(
10110)
101101&;
01000:
111000;
101010=
10001+
01010-
100001_
001101「
010010$
aliyunzixun@xxx.com
011010 咱們拿到一個摩爾斯密文,第一步先要進行分割,拆分出每一個字母。而後對照密碼錶進行翻譯便可。ui
例如,咱們有下面的密文:this
.../---/...
拆分單詞,獲得三個字母,爲: ...
---
...
。編碼
對照密碼錶翻譯,明文即爲:SOS(國際通用求救信號).net
就是這麼簡單。
Unicode 字符集擴展 摩爾斯電碼只定義了不多的基本字符,甚至基本 ASCII 碼錶都沒有補全。
咱們但願能夠編碼更多的文字,好比中文、日文甚至 Emoji 表情字符。換句話說,咱們但願能夠支持全部的 Unicode 編碼。怎麼作呢?
有一種簡單的方法,直接獲取 Unicode 值的二進制,而後 0 替換爲短信號,1 替換爲長信號。
這樣,是字典中定義的字母,則按照標準規範翻譯,不然用二級制轉換爲 Unicode,理論上就能夠支持全部字符。
該思路來源於: https://github.com/hustcc/xmorse
這是另一個摩爾斯編碼解碼實現,使用的是 Javascript 實現的。該項目中就使用了這個思路。
Java 中關於 Unicode 的一些操做 在 Java 中,一個 Unicode 被稱爲 codePoint,他是一個 int 型。
事實上,ASCII 碼錶也屬於 Unicode。可是要注意的是,一個 Unicode 字符,可能由多個 char 組成。
這裏的原理一兩句話說不清,有興趣去搜索一下 Unicode、Java 字符集相關的資源看看。
這裏只說一下在 Java 中相關轉換方法。
// String 拆分 codePoint 示例
String text = "Some Text";
for (int i = 0; i < text.codePointCount(0, text.length()); i++) {
int codePoint = text.codePointAt(i);
}
// char 數組拆分 codePoint 示例
char[] chars = new char[] {};
for (int i = 0; i < Character.codePointCount(chars, 0, chars.length); i++) {
int codePoint = Character.codePointAt(chars, i);
}
// codePoint 轉換爲 String
char[] chars = Character.toChars(codePoint);
String text = new String(chars);
// 也能夠經過 StringBuilder 來轉換
new StringBuilder().appendCodePoint(codePoint).toString(); 具體的參數含義,能夠去看一下文檔。
實現思路 有了上面的鋪墊,實現思路就很是清晰了。
咱們定義兩個 Map 結構:
一個叫作字母表,用於描述 codePoint 到摩爾斯電碼的轉換,編碼時使用;
另外一個叫作字典,用於描述摩爾斯電碼到 codePoint 的轉換,解碼時使用。
這中間涉及到 codePoint 和 String 的互相轉換問題。
遇到字典中未定義的字母,則按照 Unicode 的二進制值作轉換。
public final class MorseCoder{
private static final Map alphabets = new HashMap<>(); // code point -> morse
private static final Map dictionaries = new HashMap<>(); // morse -> code point
private static void registeMorse(Character abc, String dict){
alphabets.put(Integer.valueOf(abc), dict);
dictionaries.put(dict, Integer.valueOf(abc));
}
static {
// Letters
registeMorse('A', "01");
registeMorse('B', "1000");
registeMorse('C', "1010");
registeMorse('D', "100");
registeMorse('E', "0");
registeMorse('F', "0010");
registeMorse('G', "110");
registeMorse('H', "0000");
registeMorse('I', "00");
registeMorse('J', "0111");
registeMorse('K', "101");
registeMorse('L', "0100");
registeMorse('M', "11");
registeMorse('N', "10");
registeMorse('O', "111");
registeMorse('P', "0110");
registeMorse('Q', "1101");
registeMorse('R', "010");
registeMorse('S', "000");
registeMorse('T', "1");
registeMorse('U', "001");
registeMorse('V', "0001");
registeMorse('W', "011");
registeMorse('X', "1001");
registeMorse('Y', "1011");
registeMorse('Z', "1100");
// Numbers
registeMorse('0', "11111");
registeMorse('1', "01111");
registeMorse('2', "00111");
registeMorse('3', "00011");
registeMorse('4', "00001");
registeMorse('5', "00000");
registeMorse('6', "10000");
registeMorse('7', "11000");
registeMorse('8', "11100");
registeMorse('9', "11110");
// Punctuation
registeMorse('.', "010101");
registeMorse(',', "110011");
registeMorse('?', "001100");
registeMorse(''', "011110");
registeMorse('!', "101011");
registeMorse('/', "10010");
registeMorse('(', "10110");
registeMorse(')', "101101");
registeMorse('&;', "01000");
registeMorse(':', "111000");
registeMorse(';', "101010");
registeMorse('=', "10001");
registeMorse('+', "01010");
registeMorse('-', "100001");
registeMorse('_', "001101");
registeMorse('"', "010010");
registeMorse('$', "0001001");
registeMorse('@', "011010");
}
private final char dit; // short mark or dot
private final char dah; // longer mark or dash
private final char split;
public MorseCoder(){
this('.', '-', '/');
}
public MorseCoder(chardit,chardah,charsplit){
this.dit = dit;
this.dah = dah;
this.split = split;
}
publicStringencode(String text){
if (text == null) {
throw new IllegalArgumentException("Text should not be null.");
}
text = text.toUpperCase();
StringBuilder morseBuilder = new StringBuilder();
for (int i = 0; i < text.codePointCount(0, text.length()); i++) {
int codePoint = text.codePointAt(i);
String word = alphabets.get(codePoint);
if (word == null) {
word = Integer.toBinaryString(codePoint);
}
morseBuilder.append(word.replace('0', dit).replace('1', dah)).append(split);
}
return morseBuilder.toString();
}
publicStringdecode(String morse){
if (morse == null) {
throw new IllegalArgumentException("Morse should not be null.");
}
if (!StringUtils.containsOnly(morse, dit, dah, split)) {
throw new IllegalArgumentException("Incorrect morse.");
}
String[] words = StringUtils.split(morse, split);
StringBuilder textBuilder = new StringBuilder();
for (String word : words) {
word = word.replace(dit, '0').replace(dah, '1');
Integer codePoint = dictionaries.get(word);
if (codePoint == null) {
codePoint = Integer.valueOf(word, 2);
}
textBuilder.appendCodePoint(codePoint);
}
return textBuilder.toString();
}
} 代碼很是簡單,有了上面的鋪墊,很容易理解,不在詳細說明。
使用的時候,是這樣的:
public class MorseCoderTest{
private final MorseCoder morseCoder = new MorseCoder();
@Test
public void test0(){
String text = "Hello World!";
String morse = "...././.-../.-../---/-...../.--/---/.-./.-../-../-.-.--/";
Assert.assertEquals(morseCoder.encode(text), morse);
Assert.assertEquals(morseCoder.decode(morse), text.toUpperCase());
}
@Test
public void test1(){
String text = "你好,世界!";
String morse = "-..----.--...../-.--..-.-----.-/--------....--../-..---....-.--./---.-.-.-..--../--------.......-/";
Assert.assertEquals(morseCoder.encode(text), morse);
Assert.assertEquals(morseCoder.decode(morse), text);
}
@Test
public void test2(){
String text = "こんにちは";
String morse = "--.....-.-..--/--....-..-..--/--.....--.-.--/--.....--....-/--.....--.----/";
Assert.assertEquals(morseCoder.encode(text), morse);
Assert.assertEquals(morseCoder.decode(morse), text);
}
}
代碼已經放到 GitHub 上面了,地址在 https://github.com/TakWolf/Java-MorseCoder
而且已經作了一點微小的工做,將代碼製做成 module 發佈到了中心倉庫中(JCenter),具體使用方式去 Readme 看吧,兼容 Android 環境。
Have fun!
This blog is under a CC BY-NC-SA 3.0 Unported License
本文連接: http://blog.takwolf.com/2017/05/02/java-morse-coder/