沒錯,我是高端吃瓜玩家
前言
大家好,我是bigsai大賽哥,好久不見,甚是想念。
行了,咱們步入正軌,已經進入2022,在2021這一年,很多人的快樂消遣是在吃瓜快樂中度過的,有的作為主動吃瓜群眾第一手掌握消息,有的作為第二手或者被動吃瓜者(比如我就是)。
然而,現在吃瓜可有難度了,因為有的瓜可能是假的,某博上搜不到,在一些網站上、聊天出現一個神秘串串!!
這一串是啥玩意,驚天大瓜表達的啥意思,該怎么解讀?
這年頭,沒點知識連吃瓜群眾都當不成(手動狗頭)!作為程序員,不光要知道吃瓜的內容,還要知道吃瓜背后的技術!
好了,也不藏著掖著了,這一串就是大名鼎鼎的摩爾斯電碼,也稱摩斯密碼,當然這是以文本的形式直接展現了,你在諜戰劇中、戰爭劇中那些電報的滴滴噠噠的其實多半就是摩斯密碼,趁著這個機會,好好了解一下摩爾斯密碼吧!
摩爾斯電碼源來
摩爾斯電碼是怎么被發明的呢?是某個叫摩爾斯的天才發明的嗎?
其實在摩爾斯之前,就有非常笨重的電報機,不過這種電報機用了26根線表示26種字母(肯定沒學過計算機,妥妥的暴力美學),在實用方面很差。
在電氣時代剛流行的時代,并沒有電話手機,人們探索的第一步是如何用電去傳訊消息,在這期間摩爾斯發明了電報并且獲得了專利,并且他的團隊(有說是他的助手艾爾菲德·維爾發明的摩爾斯電碼)配套發明了一套傳輸的規則被稱為摩爾斯電碼。
在當時利用電去傳輸消息信號是非常了不起的發明,而電報機接收方會根據電報電流通過控制一直筆打印發送方按下電報機的內容,電流通過長劃線就長,電流通過時間段劃線就短,沒有電流通過紙上空白就增長。
然后接收方根據摩爾斯電碼規則轉譯成對應的字符單詞即可。主要用點( · )和劃(—)的不同排列組合表示不同的數字或字符,然后點劃之間、字符之間、字母之間停頓時間都是不同的。
摩爾斯電碼為什么用點劃兩種表示一些單詞字母呢?
大家可以考慮一下,如果一種符號確實理論上行得通,但是一個符號能夠表示的內容太少,一個連續點表示1、兩個連續點表示2、三個連續點表示3…… n種數字字符就需要n個數量符號數才能表示,這樣下去符號使用效率是非常低效了。
如果是三種符號表示,確實能夠表示的內容非常多,長度為5的符號就可以表示243個字符。能夠表達的內容其實已經遠遠超過日常使用(0-9數字,26個字母,幾個常用符號)??雌饋砗孟窈芫o湊但是三種符號訊號根本不好傳遞,很容易出現混淆問題(比如在電報等其他傳輸那么會分成長、中、短三種不容易甄別,遠不如長短兩種容易區分)。
所以2就是一個非常神奇的數字,無論在計算機還是大自然都是非常巧妙的,01、長短、快慢、高低……都可以用兩種符號表示,并且這些內容在現實生活中也是非常容易展示實現的,并且使用兩種符號能夠表示內容數量也是可以接收的,長度為5的符號就可以可以表示2^5=32種數字字符,所以這種長度還是能夠被接收的。
摩爾斯電碼藝術
我們關注摩爾斯電碼的一些含義。上面提到摩爾斯團隊早期發明的摩爾斯電碼是一些表示數字的點和劃,用一個電鍵可以敲擊出點、劃以及中間的停頓(長按,短按表示點(.)、劃(—),松開不按表示停頓),點劃、字符、單詞等時長和停頓為:
- 點( · ):1 (讀 滴 dit ,時間占據1t )
- 劃(—):111 (讀 嗒 dah ,時間占據3t )
- 字符內部的停頓(在點和劃之間):0 (時間占據1t )
- 字符間停頓:000 ( 時間占據3t )
- 單詞間的停頓:0000000 ( 時間占據7t )
有了上面的規則,我們大致能知道摩爾斯電碼長什么樣,那么怎么甄別它代表什么內容呢?這時候需要查找一本代碼表才能知道每個字母數字符號等對應的內容,其中一些主要內容如下:
來源維基百科
我靠,這個看起來好像有點記憶難度啊,確實是有難度的,根據這些內容符號的特性,有些教授給摩爾斯密碼搞成一棵二叉搜索樹讓大家更便捷記憶摩爾斯密碼,二叉樹表示的國際摩爾斯電碼。圖中每一分叉的左支為點(·),右支為劃(-),直到到達所需要表示的字符為止,這樣一棵樹可以更容易找到相似內容的聯系:
來自維基百科
不過,摩爾斯電碼還是非常有智慧的(這里不清楚是發明者這么有智慧還是記憶大師發現這么牛批的規律),摩爾斯電碼的字母和數字還有著一套象形記憶的方式,這個可不是跟咱們牛批的中文有點相似么,其具體的記憶圖為:
來源dreamstime.com
一個MORSE CODE 的摩爾斯電碼的表示和記憶為:
掌握摩爾斯密碼
好了,通過上面的介紹,想必你對摩爾斯電碼有了一定的了解,對于我們普通人來說,不需要會記住每個字母數字對應的摩爾斯電碼,我們需要掌握的就是能夠懂得摩爾斯電碼編解碼的方式和規則即可。
簡單的說,我們要掌握發送和接收的規則,將單詞字母轉成摩爾斯電碼發送,將接收的摩爾斯電碼轉成單詞單詞字母即可。
比如我們現在有:ge gie hao 這段話,其中
a : .- ;e : .;g : --.;h : .... ;i : ..;o: ---
那么紙面上對應的摩爾斯編碼為(視覺上可甄別的距離):
- --. . --. .. . .... .- ---
如果用聲音來表示(滴噠),那就是這樣的:
- --. . / --. .. . / .... .- ---
- 噠滴 滴 噠噠滴 滴滴 滴 滴滴滴滴 滴噠 噠噠噠
上面就大概是聲音的傳播過程(/表示單詞停頓時間長一些),如果用非常精確的二進制來表示,0表示沒數據,1表示有數據(電鈴按下),其實噠是滴的三倍時常,其二進制對應為:
- --. . / --. .. . / .... .- ---
- 11011101 000 1 0000000 111011101 000 101 000 1 0000000 1010101 000 10111 000 11101110111
可能看起來不是很直觀,我優化一下(實際上01是連續的沒有括號的)
- --. . / --. .. . / .... .- ---
- 11011101)000(1)0000000(111011101)000(101)000(1)0000000(1010101)000(10111)000(11101110111)
- 噠噠滴 滴 (大停頓) 噠噠滴 滴滴 滴 (大停頓) 滴滴滴滴 滴噠 噠噠噠
上面就是比較標準的摩爾斯電碼,其中三個1表示噠(三倍滴的時常),一個1表示滴,0表示沒有電流數據,這個空檔期也要把握火候的,滴噠之間是1t空閑時間,幾個滴噠組成的字符之間是3t空閑時間,幾個字符組成的一個單詞之間是7t空閑時間。
這樣,摩爾斯電碼的規則你就差不多是拿捏了。同樣給你一個摩爾斯電碼,比照電碼表也很容易給它轉成對應語句。
不過在那個時代很多電報是按照長度收費的,然而很多人就用一些簡要的單詞字母表示一句話,于是常用縮寫被很多人使用,這里不進行太多介紹,知道有點類似暗語就比如plmm:
此外,摩爾斯電碼還有一些特殊符號,表示發錯了、停止、終止、錯誤等等用來確保摩爾斯電碼發送的正確性(畢竟人肯定會有腦子糊涂或者手抖時刻就按錯了是吧)。
中文電碼
對于歐美一些國家來說,他們用那些單詞和字母使用標準的摩爾斯電碼來通訊是沒有任何問題的,畢竟26字母+數字+10個數字+少量符號就足夠了,自摩爾斯電碼在1835年發明后,一直只能用來傳送英語或以拉丁字母拼寫的文字,但是在中國甚至其他國家,怎么用電報進行通信呢?
拼音?
拼音雖然勉強傳遞一些消息,但是拼音會有很多造成很多解釋錯誤,舉個例子:
tai shuai le 可以表示太帥了,也可以表示太衰了。
ni tai mei le 可以表示你太美了,還能表示你太沒了,還能表示鎳鈦沒了……
主要是中文博大精深,所以拼音行不太通順,于是清朝時候政府雇外國人設計了中文電報,中文電碼表采用了四位阿拉伯數字作代號,簡稱“四碼電報”,從0001到9999按四位數順序排列,用四位數字表示最多一萬個漢字、字母和符號。
中文電碼,又稱標準中文電碼、中文商用電碼、中文電報碼或中文電報明碼,原本是于電報之中傳送中文信息的方法,它是第一個把漢字化作電子訊號的編碼表,大家只需要知道它在初始時候采用的這種方式就行了。
如果大家想查閱相關中文漢字對應的數字,可以在下面網站上查詢:
https://apps.chasedream.com/chinese-commercial-code/
百科對應的中文電碼也有:
https://baike.baidu.com/item/%E4%B8%AD%E6%96%87%E7%94%B5%E7%A0%81/2667759?fr=aladdin
但是中文電碼是無理碼并且數量也太多了,所以一般用戶根本沒法記憶使用,隨著通信發展、電話、手機計算機的發展,中文電碼的應用場景還是比較少的。
現在的各個網站中的中文摩斯密碼,大家實現的大多不是標準的中文電碼表對應的數字,很多是借助了其他編碼—Unicode編碼。Unicode(統一碼、萬國碼、單一碼)是計算機科學領域里的一項業界標準,包括字符集、編碼方案等。Unicode給每個字符提供了一個唯一的數字,不論是什么平臺、不論是什么程序、不論是什么語言。
所以大部分實現中文摩斯密碼的時候將對應中文字符轉成4字節unicode(UCS-4),然后再將這四個字符進行摩爾斯編碼即可。
當然,各家實現方案細節上還是有所區別的,但是問題不大,但是大部分對其編碼過程只對中文進行Unicode編碼保證英文與標準的摩爾斯電碼進行統一。
還有就是為了讓解碼過程更容易,在中文摩斯密碼中每個字符之間用\劃分,這樣通過\可以準確知道一個字符的起始位置直接進行對應轉換即可,就不用擔心因為字符、數字湊在一起造成的混淆處理了。
實現一個簡單的中文摩斯密碼
上面說了那么多,對于程序員來說,寫的code才是真的,這里面針對上面的介紹,實現一個簡單的摩斯密碼啦。
這里面實現說明一下:
- 標準形式無論中英文都以`\`作為字符劃分
- 中文的處理不采取標準中文電碼表,這里采用轉成Unicode編碼的4個16進制數字
- 不處理空格,字符間用斜杠分割(放開頭),中文字符內的Unicode字符間用空格分開(本質屬于一個中文字符內)
- 要將字符轉成大寫(或者小寫),在進行Unicode編碼時候16進制有的字母也要轉成統一大小寫
實現的代碼為:
- import java.util.HashMap;
- import java.util.Locale;
- import java.util.Map;
- //公眾號:bigsai
- //2021 1.3
- public class MorseCode {
- Map<Character, String> encMap = new HashMap<Character, String>();// 摩爾斯編碼表集合
- Map<String, Character> decMap = new HashMap<String, Character>();// 摩爾斯解碼表集合
- public static void main(String[] args) {
- MorseCode morseCode=new MorseCode();
- String val="big賽6啊 不錯 sai66";
- String encode=morseCode.Encryption(val);
- String decode=morseCode.Decryption(encode);
- System.out.println(encode);
- System.out.println(decode);
- }
- public MorseCode() {
- encMap.put('A', ".-");
- encMap.put('B', "-...");
- encMap.put('C', "-.-.");
- encMap.put('D', "-..");
- encMap.put('E', ".");
- encMap.put('F', "..-.");
- encMap.put('G', "--.");
- encMap.put('H', "....");
- encMap.put('I', "..");
- encMap.put('J', ".---");
- encMap.put('K', "-.-");
- encMap.put('L', ".-..");
- encMap.put('M', "--");
- encMap.put('N', "-.");
- encMap.put('O', "---");
- encMap.put('P', ".--.");
- encMap.put('Q', "--.-");
- encMap.put('R', ".-.");
- encMap.put('S', "...");
- encMap.put('T', "-");
- encMap.put('U', "..-");
- encMap.put('V', "...-");
- encMap.put('W', ".--");
- encMap.put('X', "-..-");
- encMap.put('Y', "-.--");
- encMap.put('Z', "--..");
- /* 數字電碼0-9 */
- encMap.put('0', "-----");
- encMap.put('1', ".----");
- encMap.put('2', "..---");
- encMap.put('3', "...--");
- encMap.put('4', "....-");
- encMap.put('5', ".....");
- encMap.put('6', "-....");
- encMap.put('7', "--...");
- encMap.put('8', "---..");
- encMap.put('9', "----.");
- /* 標點符號,可自增刪 */
- encMap.put(',', "--..--"); // ,逗號
- encMap.put('.', ".-.-.-"); // .句號
- encMap.put('?', "..--.."); // ?問號
- encMap.put('!', "-.-.--"); // !感嘆號
- encMap.put('\'', ".----.");// '單引號
- encMap.put('\"', ".-..-.");// "引號
- encMap.put('=', "-...-"); // =等號
- encMap.put(':', "---..."); // :冒號
- encMap.put(';', "-.-.-."); // ;分號
- encMap.put('(', "-.--."); // (前括號
- encMap.put(')', "-.--.-"); // )后括號
- encMap.put(' ', " "); // 留空格,這里的星號是自定義的
- for(Character ch:encMap.keySet()){
- decMap.put(encMap.get(ch),ch);
- }
- }
- boolean isChinese(char ch){
- //獲取此字符的UniCodeBlock
- Character.UnicodeBlock ub = Character.UnicodeBlock.of(ch);
- // GENERAL_PUNCTUATION 判斷中文的“號
- // CJK_SYMBOLS_AND_PUNCTUATION 判斷中文的。號
- // HALFWIDTH_AND_FULLWIDTH_FORMS 判斷中文的,號
- if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
- || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
- || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
- || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
- || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION // 判斷中文的。號
- || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS // 判斷中文的,號
- || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION // 判斷中文的“號
- ){
- return true;
- }
- return false;
- }
- //帶中文的轉成unicode
- String Encryption(String str){
- str=str.toUpperCase();
- // System.out.println(str);
- StringBuilder sBuilder=new StringBuilder();
- char chs[]=str.toCharArray();
- for(char ch:chs){
- if(ch==' '){//不處理空格
- continue;
- }
- sBuilder.append("\\");//轉義字符 字符間斜杠分開
- if(isChinese(ch)){
- String unicodeStr=Integer.toHexString(ch).toUpperCase();//轉成unicoede
- for(int i=0;i<unicodeStr.length();i++){
- sBuilder.append(encMap.get(unicodeStr.charAt(i)));
- if(i!=unicodeStr.length()-1)
- sBuilder.append(' ');//一個字符見的 摩斯密碼用空格隔開
- }
- }else {
- sBuilder.append(encMap.get(ch));
- }
- }
- return sBuilder.toString();
- }
- String Decryption(String morseCode){
- StringBuilder sBuilder=new StringBuilder();
- String morseStrs[]=morseCode.split("\\\\");//轉義字符
- for(String morseStr:morseStrs){
- //一個字符 可能中
- if(morseStr!=null&&!"".equals(morseStr)){//去掉開頭空的
- String strs[]=morseStr.split(" ");
- if(strs.length==1){//非中文直接找
- sBuilder.append(decMap.get(morseStr));
- }else {//中文先轉成4位unicode然后轉成中文
- StringBuilder unicodeStr=new StringBuilder();
- for(String uniChar:strs){
- if(uniChar!=null&&!"".equals(uniChar)){//去掉開頭空的
- unicodeStr.append(decMap.get(uniChar));
- }
- }
- int chr = Integer.parseInt(unicodeStr.toString(), 16);
- sBuilder.append((char)chr);//(char)別忘了
- }
- }
- }
- return sBuilder.toString();
- }
- }
測試為:
空格不處理
結語
到此,摩爾斯電碼的內容介紹就結束啦,對于摩爾斯電碼,我也只是介紹一點點,實現也是簡單實現一個中文的摩斯密碼轉換,有可能情況沒考慮(有錯誤歡迎指正,今天寫的比較匆忙),大家參考學習即可啦!
另外,在這個季節,祝愿大家在新的一年萬事如意,快快樂樂!【編輯推薦】