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