當前位置:首頁 » 編程軟體 » 避免編解碼

避免編解碼

發布時間: 2022-02-17 01:52:47

A. 編碼與解碼不對等

摘要 不對等的解決辦法

B. 有線滑鼠改無線滑鼠為什麼要編譯碼啊

原理使觸點導通或斷開。在實際應用中機械開頭的結構形式很多,最常用的是交叉接觸式。它的優點是結實耐用, 缺點是不防水。敲擊比較費力,打字速度快時容易漏字。不過現在比較好的機械鍵盤都增加了Click功能, click功能實際上就是從機械結構上進行了改進,加大了緩存,防止快速打字時漏掉字元。它的使用壽命5000萬到一億次左右,普通用戶10年大約鍵盤敲擊20萬次左右。所以一款好的機械鍵盤夠用一輩子了。

塑料薄膜式鍵盤
塑料薄膜式鍵盤內有四層,塑料薄膜一層有凸起的導電橡膠,當中一層為隔離層,上下兩層有觸點。通過按鍵使橡膠凸起按下,使其上下兩層觸點接觸,輸出編碼。這種鍵盤無機械磨損,可靠性較高,目前在市場占相當大的比重,不過很多JS也將這種成本相對較低的鍵盤當成電容式鍵盤。它最大的特點就是低價格, 低噪音,低成本。

導電橡膠式鍵盤
導電橡膠式鍵盤觸點的接觸是通過導電的橡膠接通。其結構是有一層帶有凸起的導電橡膠,凸起部分導電,而這部分對准每個按鍵,互相連接的平面部分不導電,當鍵帽按下去時,由於凸起部分導電,把下面的觸點按通,不按時,凸起部分會彈起。目前使用的也較多。

電容式鍵盤
電容式鍵盤它是一種類似電容式開關的原理,通過按鍵改變電極間的距離而產生電容量的變化,暫時形成震盪脈沖允許通過的條件。我們知道,電容的容量是由介質,兩極的距離及兩極的面積來決定的。所以當鍵帽按下時,兩極的距離發生變化,這就引起電容容量發生改變,當參數設計合適時,按鍵時就有輸出,而不按鍵就無輸出,這個輸出再經過整形放大,去驅動編碼器。由於電容器無接觸,所以這種鍵在工作過程中不存在磨損、接觸不良等問題,耐久性、靈敏度和穩定性都比較好。為了避免電極間進入灰塵,電容式按鍵開關採用了密封組裝。1000萬到3000萬次壽命。但目前市場上真正的電容式鍵盤並不多,大部分是前面兩種鍵盤,一款真正的電容鍵盤價格是比較高的。

無線鍵盤
當然最先進的就是無線鍵盤,顧名思義這種鍵盤與電腦間沒有直接的物理連線,通過紅外線或無線電波將輸入信息傳送給特製的接收器。接收器的連接與普通鍵盤基本相同,也只需簡單地連接到PS/2或COM口、USB口等上,購買時必須注意區別,一般無線的鍵盤在標識後有"RF"後綴(radio frequency),表示支持無線電波傳輸。現在大部分產品頻點都在900 MHz,455 MHz, 330MHz。左右。

無線鍵盤需要使用干電池供電,對於紅外線型的無線鍵盤具有較嚴格的方向性,尤其是水平位置的關系更為敏感,由於接收器接收角度有限(中心直線范圍內6公尺)在鍵盤距離接收器太近時,會出現失靈的情況,同時靈敏度低時不能快速敲鍵,否則肯定會漏字元。而採用無線電的鍵盤要靈活得多,考慮到無線電是輻射狀傳播的,為了避免在近距離內有同類型(同頻率)的鍵盤工作,導致互相干擾,一般都備有4個以上的頻道,如遇干擾可以手動轉頻。無線鍵盤為了配合移動的需要,一般體積較小巧並集成有滑鼠的功能,注意接收器和主機連接有兩個介面,一個是PS2、一個是COM口,把這兩個介面一一對應都接在主機上就可以了,但如果你不想使用鍵盤上的滑鼠,那就只需把接收器的PS2口接在主機上就可以了,COM不接!接收器不需要外接電源,而鍵盤里內置的3號鹼性電池可以正常使用3個月。

鍵盤的發展趨勢
就鍵盤的發展來看,鍵盤的鍵位是逐漸的增多(但不是無限制的增加畢竟鍵盤的面積是有限的),而且是向著多功能多媒體的方向發展。從早期推出的電腦採用83鍵鍵盤,隨後又推出了84鍵的設計標准,該標准將鍵盤分為三個區,即功能區、打字鍵區、負責游標控制和編輯的副鍵盤區。其中功能鍵區的游標鍵與數字鍵作為雙功能符號鍵使用,使用一個"Numlock"鍵來控制這兩種功能的切換。雖然兩種規格的鍵盤現在已經不多見了,但是鍵盤主要區域的劃分仍然沿用當時的標准,至今沒有什麼變化。直到1986年IBM公司推出了101鍵鍵盤,才在功能上實現了進一步的擴充,除了添加了F11、F12兩個功能鍵之外,還在鍵盤的中部多加了一組專用的游標控制和編輯的鍵,在微軟推出WIN95操作系統之後,出現Windows啟動鍵,時至今日大量帶各種附加功能鍵的鍵盤出現在我們的面前。例如Fn鍵、快捷鍵、帶滑鼠和手寫板的鍵盤等等。

常用的鍵盤的介面有AT介面、PS/2介面和USB介面,現在絕大部分主板都是提供PS/2鍵盤介面,也稱為"小口"。而兼容機尤其是較老的主板常常提供AT介面也被稱為"大口",所幸的是市場上有一種大小口鍵盤轉換連接器,售價只有區區幾元錢,它一舉解決了兩種介面鍵盤的兼容性問題。一些公司還推出了USB介面的鍵盤。根據最新公布的 PC2001規范,以後所有通過ISA 匯流排工作的介面都會隨著ISA匯流排的消亡而被USB取代。USB 允許同時將其他一些設備接入,相當於集成了一個HUB,比如可以將滑鼠接入,這實際上節約了主板的COM或PS/2口。有的鍵盤甚至本身就集成了PS/2 轉USB的電路,這樣就更方便了。目前阻礙其普及的原因還是價格太高。集成USB HUB的鍵盤,這類鍵盤大多採用USB介面,由於外設使用USB的機會增加,為了使用更多的USB設備,需要添加一種USB HUB的裝置擴展USB介面數量,但是專業的USB HUB價格比較昂貴,所以人們嘗試將USB HUB集成到鍵盤或顯示器中並得到成功。集成USB HUB的鍵盤往往自身佔用一個USB介面,用以保持鍵盤信號與主機的傳輸,同時提供2到4個USB介面供其他設備連結,簡單地說是一進多出,價格上要比專業的USB HUB便宜得多。
在單片機系統中,經常使用的鍵盤都是專用鍵盤。這類鍵盤都是單獨設計製作的,成本高,連線多,且可靠性不高。這些問題在那些要求鍵盤按鍵較多的應用系統中顯得更加突出。與此相比,在PC系統中廣泛使用的PS/2鍵盤具有價格低、通用可靠,且使用的連線少(僅使用2根信號線)的特點,並可滿足多數系統的要求。因此,在單片機系統中應用PS/2鍵盤是一種很好的選擇。
本文在分析PS/2協議和PS/2鍵盤工作原理與特點的基礎上,給出在AT89C51單片機上實現對PS/2鍵盤支持的硬體連接方法以及驅動程序的設計實現。
1PS/2協議
現在PC機廣泛採用的PS/2介面為miniDIN 6引腳的連接器。其引腳如圖1所示。
1—數據線(DATA);2—未用;3—電源地(GND);
4—電源(+5 V);5—時鍾(CLK);6—未用。
圖1PS/2連接器PS/2設備有主從之分,主設備採用female插座,從設備採用male插座。現在廣泛使用的PS/2鍵盤滑鼠均工作在從設備方式下。PS/2介面的時鍾與數據線都是集電極開路結構的,必須外接上拉電阻。一般上拉電阻設置在主設備中。主從設備之間數據通信採用雙向同步串列方式傳輸,時鍾信號由從設備產生。
(1) 從設備到主設備的通信
當從設備向主設備發送數據時,首先會檢查時鍾線,以確認時鍾線是否是高電平。如果是高電平,從設備就可以開始傳輸數據;否則,從設備要等待獲得匯流排的控制權,才能開始傳輸數據。傳輸的每一幀由11位組成,發送時序及每一位的含義如圖2所示。
圖2從設備到主設備的通信每一幀數據中開始位總是為0,數據校驗採用奇校驗方式,停止位始終為1。從設備到主設備通信時,從設備總是在時鍾線為高時改變數據線狀態,主設備在時鍾下降沿讀入數據線狀態。
(2) 主設備到從設備的通信
主設備與從設備進行通信時,主設備首先會把時鍾線和數據線設置為「請求發送」狀態。具體方式為:首先下拉時鍾線至少100 μs來抑制通信,然後下拉數據線「請求發送」,最後釋放時鍾線。在此過程中,從設備在不超過10 μs的間隔內就要檢查這個狀態。當設備檢測到這個狀態時,將開始產生時鍾信號。
此時數據傳輸的每一幀由12位構成,其時序和每一位含義如圖3所示。
圖3主設備到從設備的通信與從設備到主設備通信相比,其每幀數據多了一個ACK位。這是從設備應答接收到的位元組的應答位,由從設備通過拉低數據線產生,應答位ACK總是為0。主設備到從設備通信過程中,主設備總是在時鍾為低電平時改變數據線的狀態,從設備在時鍾的上升沿讀入數據線狀態。
2PS/2鍵盤的編碼與命令集
(1) PS/2鍵盤的編碼
現在PC機使用的PS/2鍵盤都默認採用第二套掃描碼集。該掃描碼集可參考文獻\[1\]。掃描碼有兩種不同的類型:通碼(make code)和斷碼(break code)。當一個鍵被按下或持續按住時,鍵盤會將該鍵的通碼發送給主機;而當一個鍵被釋放時,鍵盤會將該鍵的斷碼發送給主機。
根據鍵盤按鍵掃描碼的不同,在此可將按鍵分為如下幾類:
第一類按鍵,通碼為1位元組,斷碼為0xF0+通碼形式。如A鍵,其通碼為0x1C,斷碼為0xF0 0x1C。
第二類按鍵,通碼為2位元組0xE0+0xXX形式,斷碼為0xE0+0xF0+0xXX形式。如right ctrl鍵,其通碼為0xE0 0x14,斷碼為0xE0 0xF0 0x14。
第三類特殊按鍵有兩個,print screen鍵通碼為0xE0 0x12 0xE0 0x7C,斷碼為0xE0 0xF0 0x7C 0xE0 0xF0 0x12; pause鍵通碼為0x E1 0x14 0x77 0xE1 0xF0 0x14 0xF0 0x77,斷碼為空。
組合按鍵的掃描碼發送按照按鍵發生的次序,如以下面順序按左SHIFT+A鍵:1按下左SHIFT鍵,2按下A鍵,3釋放A鍵,4釋放左SHIFT鍵,那麼計算機上接收到的一串數據為0x12 0x1C 0xF0 0x1C 0xF0 0x12。
在驅動程序設計中,就是根據這樣的分類來對不同的按鍵進行不同處理的。
(2) PS/2鍵盤的命令集
主機可以通過向PS/2鍵盤發送命令來對鍵盤進行設置或者獲得鍵盤的狀態等操作。每發送一個位元組,主機都會從鍵盤獲得一個應答0xFA(「重發 resend」和「回應echo」命令例外)。下面簡要介紹驅動程序在鍵盤初始化過程中所用的指令(詳細鍵盤命令集見參考文獻\[1\]):
0xED主機在本命令後跟隨發送一個參數位元組,用於指示鍵盤上num lock, caps lock, scroll lock led的狀態;
0xF3主機在這條命令後跟隨發送一個位元組參數來定義鍵盤機打的速率和延時;
0xF4用於在當主機發送0xF5禁止鍵盤後,重新使能鍵盤。
3PS/2鍵盤與單片機的連接電路
PS/2鍵盤與AT89C51單片機的連接方式如圖4所示。P1.0接PS/2數據線,P3.2(INT0)接PS/2時鍾線。因為單片機的P1、P3口內部是帶上拉電阻的,所以PS/2的時鍾線和數據線可以直接與單片機的P1、P3相連接。
4驅動程序設計
驅動程序使用Keil C51語言,Keil uVision2編程環境。PS/2 104鍵盤驅動程序的主要任務,是實現單片機與鍵盤間PS/2通信,以及將接收到的按鍵掃描碼轉換為該按鍵的鍵值KeyVal,提供給系統上層軟體使用。
(1) 單片機與鍵盤間PS/2通信的程序設計
在PS/2通信過程中,主設備(單片機)是在時鍾信號為低時發送和接收數據信號的。因為單片機到鍵盤發送的是指令,需要鍵盤回應,所以這部分程序採用查詢方式;而單片機接收鍵盤數據時,數據線上的信號在時鍾為低時已經穩定,所以這部分程序採用中斷方式,且不需要在程序中加入延時程序。單片機的鍵盤發送介面程序見本刊網站。
(2) 鍵盤掃描碼轉換程序設計
由於鍵盤掃描碼無規律可循,因此由鍵盤掃描碼獲得相應按鍵的鍵值(字元鍵為其ASCII值,控制鍵如F1、CTRL等為自定義值),只能通過查表的方式。由於按鍵的三種類型及部分按鍵對應著兩個鍵值(如A鍵的鍵值根據CAPS和SHIFT鍵狀態有0x41(A)和0x61(a)兩種),因此綜合考慮查表轉換速度和資源消耗,設計中使用4個鍵盤表:鍵盤掃描碼轉換基本集和切換集kb_plain_map\[NR_KEYS\]與 kb_shift_map\[NR_KEYS\];包含E0前綴的鍵盤掃描碼轉換基本集和切換集kbe0_plain_map\[NR_KEYS\]與 kbe0_shift_map\[NR_KEYS\]。PS/2 104鍵盤按鍵掃描碼最大值為0x83,所以設置NR_KEYS為132。所有四個鍵盤表的定義均為如下形式:KB_MAP\[MAKE CODE\]=KEYVAL,如果掃描碼對應的按鍵為空,如KB_MAP\[0x00\],則定義相應鍵值為NULL_KEY(0x00)。以下是鍵盤掃描碼基本集的部分代碼實例:kb_plain_map\[NR_KEYS\]={……
NULL_KEY;0x2C;0x6B;0x69;0x6F;0x30;0x39;NULL_KEY;// 掃描碼0x40~0x47
file://對應按鍵空,逗號,K,I,O,0,9,空
file://對應鍵值 0x00,』,』,』k』,』i』,』o』,』0』,』9』,0x00
……};圖4硬體連接電路如此設計鍵盤轉換表的另一個好處在於,以後如需擴展支持有ACPI、Windows多媒體按鍵鍵盤時,只需要將鍵表中相應處修改即可。如ACPI power按鍵通碼為0xE0 0x37,修改kbe0_plain_map\[0x37\]=KB_ACPI_PWR即可。
特殊按鍵PAUSE使用單獨程序處理,如果接收到0xE1就轉入這段程序;而print screen鍵則將其看作是兩個通碼分別為0xE0 0x12和0xE0 0x7C的「虛鍵」的組合鍵來處理。
在驅動程序中聲明如下全局變數:led_status其bit0-scroll lock led關0、開1;bit1-num lock led關為0,開為1;bit2-caps lock led關為0,開為1;bit3~bit7總是0;agcs_status記錄左右shift ctrl gui alt狀態,bit0-左shift鍵,bit1-左ctrl鍵,bit2-左gui鍵,bit3-左alt鍵,bit4-右shift鍵,bit5-右 ctrl鍵,bit6-右gui鍵,bit7-右alt鍵,相應鍵按下則對應位為1,釋放為0。E0_FLAG接到0xE0置1;E1_FLAG接收到 0xE1置1;F0_FLAG接收到0xF0置1。按鍵鍵值通過KeyVal提供給上層使用。
PS/2鍵盤掃描碼鍵值轉換程序ps2_codetrans()流程如圖5所示。
圖5掃描碼鍵值轉換程序流程第一類按鍵的掃描碼鍵值轉換程序代碼:if (F0_FLAG) {//接收掃描碼為斷碼
switch (mcu_revchar){//處理控制鍵
case 0x11: agcs_status&=0xF7;break;//左alt釋放
case 0x12: agcs_status&=0xFE;break;//左shift釋放
case 0x14: agcs_status&=0xFD;break;//左ctrl釋放
case 0x58: if(led_status&0x04)
led_status&=0x03;//caps lock鍵
else led_status =0x04;
ps2_ledchange();
break;
case 0x59: agcs_status&=0xEF;break;//右shift釋放
case 0x77: if(led_status&0x02)
led_status&=0x05;//num lock鍵
else led_status =0x02;
ps2_ledchange();
break;
case 0x7E: if(led_status&0x01)
led_status&=0x06;//scroll lock鍵
else led_status =0x01;
ps2_ledchange();
break;
default:break;
}
F0_FLAG = 0;
}
else {//接收掃描碼為通碼
if (led_status & 0x04) caps_flag = 1; else caps_flag = 0;
if (led_status & 0x02) num_flag = 1; else num_flag = 0;
if (scga_status & 0x11) shift_flag = 1; else shift_flag = 0;
file://掃描碼鍵值轉換
if ((caps_flag == shift_flag) (!num_flag)) KeyVal=kb_plain_map\[mcu_revchar\];
else KeyVal=kb_shift_map\[mcu_revchar\];
switch(mcu_revchar){//處理控制鍵或狀態鍵
case 0x11: agcs_status = 0x08;//左alt按下
case 0x12: agcs_status = 0x01;//左shift按下
case 0x14: agcs_status = 0x02;//左ctrl按下
case 0x59: agcs_status = 0x10;//右shift按下
default: break;
}
}第二類按鍵的掃描碼鍵值轉換程序與上相似。要注意的是在退出該程序段時對E0_FLAG和F0_FLAG標志的清0。
PAUSE鍵的處理程序:如果接收到0xE1,置E1_FLAG=1,然後順次將後續接收到的7個位元組數據和PAUSE的通碼後7個位元組比較,一致則返回KeyVal=KB_PAUSE。在比較完所有7個位元組後清除E1_FLAG標志。
鍵盤初始化程序kb_init()流程:
① 上電後,接收鍵盤上電自檢通過信號0xAA,或者自檢出錯信號0xFC。單片機接收為0xAA,進入下一步,否則,進行出錯處理。
② 關LED指示,單片機發送0xED,然後接收鍵盤回應0xFA,接著發送送0x00接收0xFA。
③ 設置機打延時和速率。 單片機發送0xF3,接收0xFA,發送0x00(250ms,2.0cps),接收0xFA。
④ 檢查LED,發送0xED,接收0xFA,發送0x07(開所有LED),接收0xFA。發送0xED,接收0xFA,發送0x00(關LED),接收0xFA。
⑤ 允許鍵盤發送0xF4,接收0xFA。
鍵盤LED改變ps2_ledchange()函數流程:發送0xED→接收0xFA→發送led_status→接收0xFA。
結語
該驅動程序經Keil uVision2編譯,在AT89C51單片機上運行通過,實現了對PS/2 104鍵盤的支持,以及對字元按鍵大小寫切換,num lock切換,控制鍵及組合按鍵的支持。該程序對其他嵌入式或單片機系統中PS/2鍵盤的應用也有借鑒意義。
參考文獻
1Adam Chapweske. The ATPS/2 Keyboard Interface.
2Adam Chapweske. PS/2 Mouse/Keyboard Protocol.
3Network Technologies Incorporated. PS/2 Keyboard & Mouse Protocols.
4 Linux 2.4.10內核程序 defkeymap.c dn_keyb.c kbd.c keybdev.c keyboard.c kbd_kern.h kd.h keyboard.h
PS/2幀的第一位是起始位,為0,然後是8位數據位,發送鍵盤掃描碼的一個位元組(掃描碼為1-4個位元組),然後是奇偶校驗位,最後是停止位,為1。這些是在數據線(即1號引腳線)上發送的。無鍵按下時,數據線和始終線都保持為1。當有鍵按下時,時鍾線CLOCK送出脈沖,同時數據線送出數據。主機(此處是89c51 MCU)在始終脈沖的下降沿對數據線采樣獲得數據。鍵盤掃描碼包括通碼和斷碼,當鍵按下時發送通碼,抬起時發送斷碼。更詳細的內容可參考所附的《PS/2 技術參考》。
根據上述原理,我們可以將鍵盤的脈沖線接至89c51的外部中斷輸入口(INT0或INT1),當鍵按下和抬起時有脈沖產生,此脈沖引發MCU 中斷。將鍵盤的DATA線連至89c51的輸入口(如P1.0)。在中斷處理程序中,從輸入口讀入數據,然後通過循環移位對讀進的數據位進行處理,1(起始位)、10(奇偶校驗)、11(停止位)可拋棄,如不嫌麻煩也可將奇偶校驗位加以應用。當一個數據幀收完後,將處理後剩下的2-9位(即掃描碼)通過串口發至PC機,通過PC機的串口監視軟體(如「串口調試助手」)來查看。硬體連線和源碼如下:

源碼:

ORG 0000H
AJMP MAIN;轉入主程序
ORG 0003H ;外部中斷P3.2腳INT0入口地址
AJMP INT ;轉入外部中斷服務子程序
;以下為主程序進行CPU中斷方式設置
MAIN:MOV SCON,#50H;設置成串口1方式
MOV TMOD,#20H;波特率發生器T1工作在模式2上
MOV PCON,#80H;波特率翻倍為2400x2=4800BPS
MOV TH1,#0F3H;預置初值(按照波特率2400BPS預置初值)
MOV TL1,#0F3H;預置初值(按照波特率2400BPS預置初值)

SETB EA ;打開CPU總中斷請求
SETB IT0 ;設定INT0的觸發方式為脈沖負邊沿觸發
SETB EX0 ;打開INT0中斷請求
SJMP $

INT: CLR EA ;暫時關閉CPU的所有中斷請求
CJNE R0,#0,L1
L3: INC R0
SJMP L5
L1: CJNE R0,#9,L2
SJMP L3
L2: CJNE R0,#10,L4
SETB TR1;啟動定時器T1
MOV SBUF,A
MOV R0,#0
L5: SETB EA ;允許中斷
RETI ;退出子程序
L4: MOV C,P1.0
RRC A
SJMP L3

END
搞定後,當按下和釋放鍵時,會在PC機上顯示其掃描碼。
通電時鍵盤會自檢,此時鍵盤上三個燈全亮,自檢完成後熄滅,並向主機發送十六進制字元AA.。

C. 編碼和解碼的區別有哪些

如兩個人面對面地交談,講者把信息變成適於空氣傳遞的聲信號,變成語言,就得經過大腦用字片語成句子。這個過程就是編碼。聽者的耳朵接收到聲音信號,經過大腦的理解,明白話音、句子所包含的意思。這個過程就是解碼。

D. 調制 解調 編碼 解碼的關系與區別

調制解調,不同信號轉換,數字音頻轉換,和紅燈停綠燈行差不多編碼解碼,數字信號間的轉換,和加密解密差不多

E. 簡述一下生活中的編解碼現象。很急!

超市條形碼,QQ二維碼

F. 生活中遇到的編解碼現象

電話,手機,電腦上圖片的顯示,電信號轉換為音響音信號

G. 糾錯編解碼器的研製

還不會,你能等嗎?
我一個月左右可能可以學會,不過還是沒看懂你的(7,4)漢明碼的糾錯編解碼器的意思

H. 何謂編碼何謂解碼

編碼 通俗來講就是 把特定意義的信息按照特定規律轉換成另一種信息
解碼 就是把信息按照某種規律翻譯出本來意義的信息

I. 為什麼要對信息進行編碼

編碼是將源對象內容按照一種標准轉換為一種標准格式內容。
解碼是和編碼對應的,它使用和編碼相同的標准將編碼內容還原為最初的對象內容。
編解碼的目的最初是為了加密信息,經過加密的內容不知道編碼標準的人很難識別,已經有數千年歷史了。
而現在編解碼種類非常多,主要目的則是為了信息交換。
除了加密,目前程序中常見的如字元編解碼,HTML編解碼,URL編解碼,郵件編解碼,多媒體編解碼等。編碼是為了符合傳輸的要求,解碼是為了還原成我們能識別的信息。
例如字元編解碼,字元編碼在一系列數字與人們將文本輸入到計算機中時希望看到的字元之間提供映射。因為世界上有不同的語言和文字,所以需要將不同的文字編碼以通過計算機處理和傳輸。
再比如多媒體編解碼,因為有多種不同格式的圖像聲音,所以它們各自有自己編解碼標准。

J. 哈夫曼編、解碼器

這個是我課程設計弄的,也是哈弗曼編碼解碼器
#include<iostream>
#include<stdlib.h>
#include<string.h>
using namespace std;

typedef struct{
int weight;
int parent,lchild,rchild;
int asc;
}HTNode,*HuffmanTree; //定義赫夫曼存儲結構

struct node{
int ASCII;
int n;
};
struct node a[127];
struct node w[127];
//一些全局變數
int count;
HTNode H_T[250];
char ** HC;
char filename[20];

//函數聲明
void menu1(); //菜單1
HuffmanTree HeapSort(HuffmanTree HT,int length); //堆排序
void MinHeapify(HuffmanTree HT, int k,int len); //調整成一個小頂堆
void buildMinHeap(HuffmanTree HT,int len); //建一個小頂堆
void swap(HTNode &t1,HTNode &t2); //交換兩結構體
void savefile(); //把輸入的英文文章保存到文件
void loadfile(); //從文件中讀取文章
HuffmanTree CreateHuffman(HuffmanTree &HT,struct node *w,int n); //建立赫夫曼數並存入文件
void BianMa(HuffmanTree HT,int n ); //字元編碼
void BianMa_all(HuffmanTree HT,char**HC,char *filename); //整篇文章編碼
int loadfile2(); //從文件讀入文章
void JieMa(); //解碼

//主函數
int main()
{
char s;
while(s!='0')
{
system("cls");
cout<<"\n\n\n";
cout<<"\t\t\t\t赫夫曼編碼/解碼器"<<endl<<endl<<endl<<endl<<endl;
cout<<"\t\t\t\t 1.編碼"<<endl<<endl<<endl<<endl;
cout<<"\t\t\t\t 2.解碼"<<endl<<endl<<endl<<endl;
cout<<"\t\t\t\t 0.退出"<<endl<<endl<<endl<<endl;
cout<<"\t請輸入0—2進行選擇,按回車確認";
cin>>s;
switch(s)
{
case '1': menu1(); break;
case '2':
{
system("cls");
JieMa();
system("pause");
break;
}

}

}
}

//菜單1
void menu1(){
char s;
int i;
int a;
char c;
char fpname[20]="article.txt";
HuffmanTree HT;
while(s!='0'){

system("cls");
cout<<"\n\t\t\t\t編碼界面";
cout<<"\n\n\n\t\t\t\t1.輸入英文文章"<<endl;
cout<<"\n\n\t\t\t\t2.從文件中讀入文章"<<endl;
cout<<"\n\n\t\t\t\t0.返回上一層"<<endl;
cout<<"\n\t請輸入0—2進行選擇,按回車確認";
cin>>s;
switch(s){
case'1':
system("cls");
savefile();
loadfile();
CreateHuffman(HT,w,count);
BianMa(HT,count);
BianMa_all(HT,HC,fpname);
system("cls");
cout<<"出現字元種類共計:";
cout<<count<<endl;
for(i=1;i<=count;i++)
{ a=HT[i].asc;
c=(char)a;

cout<<"________________________________________________________________________________"<<endl;
cout<<"\t\t\t字元:";
cout<<c<<endl;
cout<<"\t\t\tASCII碼:";
cout<<a<<endl;
cout<<"\t\t\t頻數:";
cout<<HT[i].weight<<endl;
cout<<"\t\t\t赫夫曼編碼:";
cout<<HC[i]<<endl;

}
cout<<"________________________________________________________________________________";
cout<<"\n\t\t整篇文章的編碼已存入文件「赫夫曼編碼.txt」"<<endl;

system("pause");
break;

case'2':
system("cls");
if(loadfile2())
{ system("pause");
return;}
CreateHuffman(HT,w,count);
BianMa(HT,count);
BianMa_all(HT,HC,filename);
system("cls");
cout<<"出現字元種類共計:";
cout<<count<<endl;
for(i=1;i<=count;i++)
{ a=HT[i].asc;
c=(char)a;

cout<<"________________________________________________________________________________"<<endl;
cout<<"\t\t\t字元:";
cout<<c<<endl;
cout<<"\t\t\tASCII碼:";
cout<<a<<endl;
cout<<"\t\t\t頻數:";
cout<<HT[i].weight<<endl;
cout<<"\t\t\t赫夫曼編碼:";
cout<<HC[i]<<endl;

}
cout<<"________________________________________________________________________________";
cout<<"\n\t\t整篇文章的編碼已存入文件「赫夫曼編碼.txt」"<<endl;
system("pause");
break;
}

}

}

//交換結構體
void swap(HTNode &t1,HTNode &t2)
{
HTNode m;

m = t1;
t1 = t2;
t2 = m;
}

//從鍵盤輸入文章並保存
void savefile(){

FILE *fp;
char article;
if((fp=fopen("article.txt","w"))==NULL){

printf("打開文件不成功!");
exit(0);
}
cout<<"請輸入英文文章,以#結束:";
getchar();
article=getchar();
while(article!='#'){

fputc(article,fp);

article=getchar();
}
fclose(fp);
}

//從文件讀取文章,並統計字元出現頻數
void loadfile(){

FILE *fp;
char ch;
int i,k,j=0;
count=0;
for(i=0;i<=127;i++) //把所有字元的ascii碼存在數組
{ a[i].ASCII=i;
a[i].n=0;

}
if((fp=fopen("article.txt","r"))==NULL){

printf("打開文件不成功!");
exit(0);
}
ch=fgetc(fp);
k=(int)ch;
a[k].n++;
while(!feof(fp)){
ch=fgetc(fp);
k=(int)ch;
a[k].n++;
}
fclose(fp);

for(i=0;i<=127;i++) //統計字元種類總數
{
ch=(char)i;
if(a[i].n){
count++;
}
}
for(i=0;i<=127;i++)
{
for(;j<count;)
{
if(a[i].n)
{
w[j].n=a[i].n;
w[j].ASCII=a[i].ASCII;
j++;
break;
}
else break;
}
}

}

//調整為小頂堆
void MinHeapify(HuffmanTree HT, int k,int len)
{
int left=k*2;
int right=k*2+1;
int large;
int l=len;

large = k;
if (left<=l&&HT[left].weight<HT[large].weight)
large = left;

if (right<=l&& HT[right].weight<HT[large].weight)
large=right;

if (large!=k)
{
swap(HT[k],HT[large]);
MinHeapify(HT,large,l);
}
}

//建立小頂堆
void buildMinHeap(HuffmanTree HT,int len)
{
int i;
for (i=len/2;i>=1;i--)
{
MinHeapify(HT,i,len);
}
}

//堆排序
HuffmanTree HeapSort(HuffmanTree HT,int length)
{
int i;
int l=length;
buildMinHeap(HT,length);
for (i=length;i>= 2;i--)
{
swap(HT[1],HT[i]);
length--;
MinHeapify(HT,1,length);
}

return HT;
}

//建立赫夫曼數
HuffmanTree CreateHuffman(HuffmanTree &HT,struct node *w,int n)
{
int i,m,s1,s2,k1,k2,j,x,a;
FILE *fp,*fp2;

if(n<=1) return HT;
m=2*n-1;
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//0不用

for(i=1,j=0;i<=n;i++,j++)
{ HT[i].asc=w[j].ASCII;
HT[i].weight=w[j].n;
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
for(;i<=m;i++)
{ a=250+i;
HT[i].asc=a;//父親節點的asc可以是大於127的任意值
HT[i].weight=0;
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
for(i=1;i<=n;i++){

H_T[i].asc=HT[i].asc;
H_T[i].parent=HT[i].parent;
H_T[i].lchild=HT[i].lchild;
H_T[i].rchild=HT[i].rchild;
H_T[i].weight=HT[i].weight;
}

for(i=n+1,x=n;i<=m;i++,x--)
{

HeapSort(H_T,x);
k1=H_T[x].asc;
k2=H_T[x-1].asc;
for(j=1;j<=127;j++)
{
if(HT[j].asc==k1)

}

for(j=1;j<=127;j++)
{
if(HT[j].asc==k2)

}

HT[s2].parent=i;
HT[s1].parent=i;
HT[i].lchild=s1;
HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;

H_T[x-1].asc=HT[i].asc;
H_T[x-1].lchild=HT[i].lchild;
H_T[x-1].parent=HT[i].parent;
H_T[x-1].rchild=HT[i].rchild;
H_T[x-1].weight=HT[i].weight;

}
if((fp2=fopen("count.txt","w"))==NULL) //保存赫夫曼樹
{
cout<<"文件打開不成功!"<<endl;
exit(0);
}
fputc(count,fp2);
if((fp=fopen("HuffmanTree.dat","wb"))==NULL)
{ cout<<"文件打開不成功!"<<endl;
exit(0);

}

for(i=1;i<=(2*count-1);i++){
fwrite(&HT[i],sizeof(HTNode),1,fp);
}
fclose(fp);
fclose(fp2);
return HT;

}

//逆向編碼
void BianMa(HuffmanTree HT,int n){
char *cd,temp;

int c,f,i,j,len,p,q;

cd=(char *)malloc(n*sizeof(char));
HC=(char * *)malloc(n*sizeof(char*));
for(i=1;i<=n;i++){
for(c=i,f=HT[i].parent,j=0;f!=0;c=f,f=HT[f].parent,j++)
{ if(HT[f].lchild==c) cd[j]='0';
else cd[j]='1';
if(HT[f].parent==0)
cd[j+1]='\0';

}

len=strlen(cd);
for(p=0,q=len-1;p<=q;p++,q--)
{
temp=cd[q];
cd[q]=cd[p];
cd[p]=temp;
}
cd[len]='\0';
HC[i]=(char*)malloc((len+10)*sizeof(char));
strcpy(HC[i],cd);

}

}

//整篇文章編碼,並存入文件
void BianMa_all(HuffmanTree HT,char**HC,char *filename){
char ch;
int k,i;
FILE *fp,*fp2;

char code[100];
if((fp=fopen(filename,"r"))==NULL){

printf("打開文件不成功!");
exit(0);
}
if((fp2=fopen("赫夫曼編碼.txt","w"))==NULL){

printf("打開文件不成功!");
exit(0);
}
ch=fgetc(fp);
k=(int)ch;
while(!feof(fp))
{

for(i=1;i<=count;i++)
{
if(k==HT[i].asc)
strcpy(code,HC[i]);

}
fputs(code,fp2);
ch=fgetc(fp);
k=(int)ch;

}
fclose(fp);
fclose(fp2);

}

void JieMa(){
int i,k,a,t,n=0;
FILE *fp1,*fp2,*fp3;
char ch,c;
HuffmanTree ht;
if((fp3=fopen("count.txt","r"))==NULL) //從文件讀出字元總數
{
printf("打開文件不成功!");
exit(0);
}
n=fgetc(fp3);
ht=(HuffmanTree)malloc(2*n*sizeof(HTNode));
if((fp1=fopen("赫夫曼編碼.txt","r"))==NULL)
{

printf("打開文件不成功!");
exit(0);
}

if((fp2=fopen("HuffmanTree.dat","rb"))==NULL)
{ cout<<"文件打開不成功!"<<endl;
exit(0);

}
for(i=1;i<=2*n-1;i++)

fread(&ht[i],sizeof(HTNode),1,fp2);

for(i=1;i<=2*n-1;i++)
{
if(ht[i].parent==0)
}

ch=fgetc(fp1);
while(!feof(fp1)){
if(ch=='0')
{
k=ht[k].lchild;
if(ht[k].lchild==0)
{a=ht[k].asc;
c=(char)a;
printf("%c",c);;

k=t;
}
}
if(ch=='1')
{
k=ht[k].rchild;
if(ht[k].lchild==0)
{ a=ht[k].asc;
c=(char)a;
printf("%c",c);

k=t;
}

}

ch=fgetc(fp1);
}
fclose(fp1);
fclose(fp2);

}

//讀取文件中的文章,可自己選擇文件
int loadfile2(){

FILE *fp;
char ch;
int i,k,j=0;
count=0;
for(i=0;i<=127;i++)
{ a[i].ASCII=i;
a[i].n=0;

}
cout<<"\n\n\n\t\t\t請輸入你要打開的文件名:";
cin>>filename;
if((fp=fopen(filename,"r"))==NULL){

printf("打開文件不成功!");
return 1;
}
ch=fgetc(fp);
k=(int)ch;
a[k].n++;
while(!feof(fp)){
ch=fgetc(fp);
k=(int)ch;
a[k].n++;
}
fclose(fp);

for(i=0;i<=127;i++){
ch=(char)i;
if(a[i].n){
count++;
}
}
for(i=0;i<=127;i++)
{
for(;j<count;)
{
if(a[i].n)
{
w[j].n=a[i].n;
w[j].ASCII=a[i].ASCII;
j++;
break;
}
else break;
}
}
return 0;
}

熱點內容
安卓機哪裡看型號 發布:2024-03-29 15:59:40 瀏覽:281
cad解壓錯誤 發布:2024-03-29 15:01:45 瀏覽:79
存儲指令集 發布:2024-03-29 14:39:27 瀏覽:649
資料庫表刪除數據 發布:2024-03-29 14:39:26 瀏覽:367
出c語言整除 發布:2024-03-29 14:28:22 瀏覽:572
芬尼壓縮機 發布:2024-03-29 14:24:11 瀏覽:464
電腦數據實時上傳本地伺服器軟體 發布:2024-03-29 14:07:57 瀏覽:920
尋秦記源碼 發布:2024-03-29 13:56:17 瀏覽:496
linux的備份命令 發布:2024-03-29 13:41:22 瀏覽:383
csgo建議什麼配置 發布:2024-03-29 13:31:44 瀏覽:980