




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第C++實戰(zhàn)之二進制數(shù)據(jù)處理與封裝目錄前言什么是二進制數(shù)據(jù)處理二進制數(shù)據(jù)封裝二進制數(shù)據(jù)思路1:基于配置文件思路2:基于數(shù)據(jù)底層存儲方式
前言
最近在研究所做網(wǎng)絡(luò)終端測試的項目,包括一些嵌入式和底層數(shù)據(jù)幀的封裝調(diào)用。之前很少接觸對二進制原始數(shù)據(jù)的處理與封裝,所以在此進行整理。
以下例子主要以c++語言進行說明。
什么是二進制數(shù)據(jù)
在電腦上一切數(shù)據(jù)都是通過二進制(0或1)進行存儲的,通過多位二進制數(shù)據(jù)可以進而表示整形、浮點型、字符、字符串等各種基礎(chǔ)類型數(shù)據(jù)或者一些更復(fù)雜的數(shù)據(jù)格式。
針對日常中一般的需求進行編程,我們通常無需關(guān)注底層的二進制數(shù)據(jù)。但如果要處理二進制文件(音頻、視頻、圖片等)、設(shè)計空間上更高效的數(shù)據(jù)結(jié)構(gòu)(網(wǎng)絡(luò)數(shù)據(jù)幀、字節(jié)碼、protobuf)或者處理某些底層時,需要我們處理這些二進制數(shù)據(jù)。
計算機中,稱每一個二進制位為比特(bit,也稱:位),是計算機中的最小存儲單位。
每8比特組成一個字節(jié)(byte),一般是計算機實際存儲和處理的最小單位(可以是它的倍數(shù)),也就是說,計算機是以字節(jié)為最小單位分配空間或進行計算的,不能分配比字節(jié)更小的存儲空間(如,最小的數(shù)據(jù)類型是char,長度1字節(jié),不支持申請6比特存儲空間)或者直接處理小于字節(jié)單位的數(shù)據(jù)(如,兩個4比特的數(shù)據(jù)相加減)。
若干字節(jié)構(gòu)成一個計算機字(簡稱:字,word),表示計算機一次性處理事務(wù)的固定長度二進制數(shù)據(jù),字的位數(shù)為字長。計算機是以字為單位處理或運算的,兩個常見的概念是CPU位數(shù)和操作系統(tǒng)位數(shù)。
CPU的位數(shù)就是指CPU執(zhí)行一次指令能處理的最大位數(shù)(一個字長),和CPU中的寄存器的位數(shù)對應(yīng)。其中,地址寄存器MAR限制了計算機的尋址范圍,數(shù)據(jù)寄存器MDR限制了一次處理的數(shù)據(jù)長度。更多的位數(shù)帶來了更大的尋址空間和更強的運算能力。
說明:尋址范圍不等于內(nèi)存大小,尋址對象有內(nèi)存條、顯卡內(nèi)存、聲卡、網(wǎng)卡和其他設(shè)備。之所以常把尋址范圍當(dāng)作內(nèi)存上限,是因為內(nèi)存是CPU的主要尋址對象。
這里解釋一下常見的指令架構(gòu):x86是intel推出的一種指令集架構(gòu)(復(fù)雜指令集CISC架構(gòu)),一開始只有32位的,叫x86_32;后來AMD公司推出了兼容x86_32的64位指令集amd64,被業(yè)界接受,intel將其改名為x86_64,簡稱x64,而x86_32和x86_64可統(tǒng)稱為x86。與x86相對的是基于精簡指令集RISC架構(gòu)的ARM指令集架構(gòu),多用于移動設(shè)備。
操作系統(tǒng)基于CPU指令集實現(xiàn),所以操作系統(tǒng)位數(shù)也直接對應(yīng)CPU位數(shù)。由于CPU指令集的向下兼容性,所以32位操作系統(tǒng)也可以運行在64位的CPU上,但反過來不行。操作系統(tǒng)對軟件提供了向下兼容的能力,64位的操作系統(tǒng)支持64和32位的程序,但32位的操作系統(tǒng)只支持32位的程序。
處理二進制數(shù)據(jù)
在大多語言中,最小的數(shù)據(jù)類型是char,一個字節(jié),二進制數(shù)據(jù)多用unsignedchar表示,并寫作uint8。語言底層常把它當(dāng)作int進行運算。
二進制常數(shù)以0b開頭,如:0b001。二進制數(shù)據(jù)也常用8進制(以0開頭)和16進制(以0x開頭)表示,如:0257(175,八進制)、0x1f(31,16進制)。8進制1個數(shù)字表示3位二進制數(shù)據(jù),16進制1個數(shù)字表示4位二進制數(shù)據(jù),一個字節(jié)可以用2個16進制數(shù)表示。
若要處理小于一字節(jié)的數(shù)據(jù),就要使用位運算符(、|、^、~、、)。
位運算符描述運算規(guī)則用途與兩個位都為1時,結(jié)果才為1二進制位清零或得到指定位數(shù)據(jù)|或兩個位都為0時,結(jié)果才為0二進制位設(shè)置為1;與對應(yīng)位為0的數(shù)據(jù)相加^異或兩個位相同為0,相異為1反轉(zhuǎn)指定位~取反0變1,1變0二進制位全部取反左移各二進位全部左移若干位,高位丟棄,低位補0求x2nx2n;將數(shù)據(jù)移到高位右移各二進位全部右移若干位,對無符號數(shù),高位補0,有符號數(shù),各編譯器處理方法不一樣,有的補符號位(算術(shù)右移),有的補0(邏輯右移)求x/2nx/2n;將數(shù)據(jù)移到低位
舉個例子,判斷某個字節(jié)的第3位是否是1:
//先清0其他位,再判斷是否等于0b100
boolisOne=(byte0b100)==0b100;
再舉個例子,計算機網(wǎng)絡(luò)IP協(xié)議中的controlflag和fragmentoffset合起來存儲在IP頭部的第7、8字節(jié),flag占前三位,后13位為fragmentoffset,可以通過以下運算獲得flag和offset:
//獲得flag要截取byte7前3位數(shù)據(jù):先清空后5位,保留前3位數(shù)據(jù),再右移5位將前3位數(shù)據(jù)移到起始
uint8_tflag=(byte70b11100000)5;
//此處以大端存儲,獲得offset要截取byte7的低5位作為高位,byte8作為低位,求和:先清空byte7前3位,保留后5位數(shù)據(jù),把它移到高8位上,再通過全0的低8位與byte8按位求或來求二者之和
((byte70b00011111)8)|byte8;
補充說明,當(dāng)需要多個字節(jié)表示一個數(shù)據(jù)類型時,需要定義數(shù)據(jù)的高位字節(jié)是存儲在高位地址空間還是低位地址空間,這就是大小端的定義。大端指高位字節(jié)存在低位地址,這是人的手寫習(xí)慣;小端指低位字節(jié)存高位地址。在處理用多個字節(jié)表示的數(shù)據(jù)時,首先要搞清楚數(shù)據(jù)是大端還是小端。
所以,我們可以基于上述知識寫一個無符號整形與字節(jié)流相互轉(zhuǎn)換的通用方法:
//true為大端,低位地址存高位字節(jié)
boolENDIAN=true;
*將data轉(zhuǎn)換為無符號整形數(shù)字(無符號char,short,int,long,longlong等)
*@tparamT目標類型,默認為uint32_t
*@paramdata載荷數(shù)據(jù)byte數(shù)組
*@paramvalueSize數(shù)據(jù)長度,單位:byte,-1表示根據(jù)T類型自動計算
*@paramdefault_value默認值,默認為0
*@return根據(jù)data轉(zhuǎn)換的無符號整形數(shù)據(jù)
templatetypenameT=uint32_t
TpayloadToUnsignedInt(std::vectoruint8_tdata,intvalueSize=-1,Tdefault_value=uint32_t(0)){
if(valueSize==-1)valueSize=sizeof(T);
if(valueSizedata.size())returndefault_value;
Tvalue=0;
for(inti=0;ivalueSize;i++){
if(ENDIAN){
value|=(data[i]0xff)((valueSize-1-i)3);
}else{
value|=(data[i]0xff)(i3);
returnvalue;
*無符號整形轉(zhuǎn)換為載荷byte數(shù)組
*@paramvalue無符號整形數(shù)據(jù)
*@paramvalueSize數(shù)據(jù)長度,單位:byte,-1表示根據(jù)T類型自動計算
*@return載荷byte數(shù)組
templatetypenameT
std::vectoruint8_tuintToPayload(Tvalue,intvalueSize=-1){
if(valueSize==-1)valueSize=sizeof(T);
std::vectoruint8_tdata(valueSize,0);
for(inti=0;ivalueSize;i++){
if(ENDIAN){
data[i]=(value((valueSize-1-i)3))0xff;
}else{
data[i]=(value(i3))0xff;
returndata;
}
封裝二進制數(shù)據(jù)
掌握了二進制數(shù)據(jù)的處理方法,接下來就是對二進制數(shù)據(jù)的封裝,將其封裝為人可以理解的對象。
二進制數(shù)據(jù)通常以uint8_t數(shù)組表示,不同位有不同的含義,需要根據(jù)實際含義進行解析后得到有意義的目標信息。所以重點就是描述每一位的含義,并基于該描述解析二進制數(shù)據(jù),提供二進制數(shù)據(jù)與有含義的對象的相互轉(zhuǎn)換。
思路1:基于配置文件
此處以自定義的二進制指令封裝為例進行說明(項目地址),但該配置項目適用于任意二進制數(shù)據(jù)封裝場景。面對這個需求,首先想到的是通過配置文件描述二進制流每一位的含義,加載配置文件后根據(jù)一些過濾條件配置確定當(dāng)前二進制流段實際對應(yīng)的配置并解析為字典。
由于項目包括一些嵌入式的內(nèi)容,需要把所有文件編譯后燒入板子,不支持存儲普通文件格式的配置文件,所以采用變量形式的配置,全局聲明配置的類型信息和配置對象(cmd_manager),項目內(nèi)任意位置定義該配置對象即可。在其他場景也可選擇Json、xml等配置格式。
本文設(shè)計的配置對象定義方式如下:
/**
*載荷配置項
constCmdManagercmd_manager={2,{//指令個數(shù),下面是每一個指令的配置
{"TCRQ",3,{//配置項名,配置項對應(yīng)的字段數(shù)
{"TE_SEQ_NO",-1,FT_SHORT,0},//具體配置項內(nèi)字段配置(字段名,字段偏移,字段類型,配置項該字段過濾條件
{"CMD",-1,FT_CHARS_4,"TCRQ"},//配置項要求該字段等于"TCRQ",數(shù)據(jù)不滿足則不匹配該配置項
{"REPEAT_COUNT",-1,FT_SHORT,0}}}
}};
項目會自動加載該配置對象,之后針對原始二進制數(shù)據(jù)通過PayloadObjectMapFactory工廠匹配對應(yīng)配置并生成數(shù)據(jù)對象,可從數(shù)據(jù)對象獲得該對象類型(配置項名)并讀寫其中的字段值。或者指定配置項創(chuàng)建空的數(shù)據(jù)對象,進行數(shù)據(jù)設(shè)置后獲得其原始二進制數(shù)據(jù)載荷。
評價
該思路通過配置文件可以自由且動態(tài)的調(diào)整解析方式,易于復(fù)用、拓展或調(diào)整。其難點在于配置格式的設(shè)計,同時字典類型數(shù)據(jù)無法如直接聲明類型結(jié)構(gòu)那樣清晰易用。
思路2:基于數(shù)據(jù)底層存儲方式
此處以計算機網(wǎng)絡(luò)數(shù)據(jù)幀封裝為例進行說明。c++底層對對象/結(jié)構(gòu)體的成員字段采用類型對齊連續(xù)存儲方式,使用該特性可以基于實際含義自然聲明、使用字段,同時可以直接作為二進制數(shù)據(jù)流處理。實現(xiàn)示例如下:
/**
*數(shù)據(jù)抽象類,提供二進制流到對象的相互轉(zhuǎn)化能力
*內(nèi)部類,只復(fù)用代碼,不用于多態(tài)
*@tparamsize數(shù)據(jù)字節(jié)長度
templateintsize
classDataType{
public:
DataType(){resetData();}
//初始化所有數(shù)據(jù)
voidresetData()const{memset((void*)(this),0,size);}
//從二進制流加載數(shù)據(jù)
boolloadData(conststd::vectoruint8_tdata,intstartIndex=0){
auto*p=(uint8_t*)this;//將自身當(dāng)作二進制數(shù)組處理
for(inti=0;isize;i++){
*p=data[i+startIndex];
p++;
returntrue;
//基于自身生成新的二進制數(shù)據(jù)流
[[nodiscard]]std::vectoruint8_tcreateData()const{
std::vectoruint8_tresult;
autop=(uint8_tconst*)this;
for(inti=0;isize;i++){
result.push_back(*p);
p++;
returnresult;
[[nodiscard]]intgetSize()const{returnsize;}
//以順序聲明方式定義具體的二進制數(shù)據(jù)類型,支持嵌套聲明
classMACHeader:publicDataType14{
public:
//通過上述無符號整形與字節(jié)流相互轉(zhuǎn)化的方法將netType的讀寫進行封裝
[[nodiscard]]uint16_tgetNetType()const{
returnpayloadToUnsignedInt(std::vectoruint8_t(netType.begin(),netType.end()),2,uint16_t(0));
voidsetNetType(uint16_t_netType){
autodata=uintToPayload(_n
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 中國核酸保存試劑行業(yè)市場前景預(yù)測及投資價值評估分析報告
- 2025秋五年級語文上冊統(tǒng)編版-【22 鳥的天堂】交互課件
- 文具店計劃書
- 汽修學(xué)徒安全合同協(xié)議書
- 中國泡沫塑料項目商業(yè)計劃書
- 2025年中國細胞灌注培養(yǎng)基行業(yè)市場占有率及投資前景預(yù)測分析報告
- 環(huán)保項目計劃書
- 資金入股投資合同協(xié)議書
- 洗臉吧項目計劃書
- 合作盈利合同協(xié)議書模板
- 高中生物必修一實驗通知單
- 運動員健康證明表
- 課件:第四章 社會工作項目的執(zhí)行(《社會工作項目策劃與評估》課程)
- 冷庫施工組織設(shè)計施工方案
- 咯血診斷與治療課件
- 醫(yī)學(xué)影像專業(yè)個人簡歷
- 檢驗科 醫(yī)院感染管理質(zhì)量督查評分表
- 獨立性檢驗 公開課比賽一等獎-完整版獲獎?wù)n件
- 網(wǎng)絡(luò)信息系統(tǒng)癱瘓演練PDCA改進
- 高分子材料成型加工基礎(chǔ)添加劑及配方設(shè)計課件
- 水泥水化熱實驗原始記錄
評論
0/150
提交評論