孫立哲
摘要:基于某產(chǎn)品測試實(shí)際需求,為了實(shí)現(xiàn)對大數(shù)據(jù)量TLV(Type,Length,Value)文件數(shù)據(jù)正確性的快速自動校驗(yàn),采用Java編程語言,基于面向?qū)ο蠹夹g(shù)以及內(nèi)存映射技術(shù),完成了對具有二級嵌套結(jié)構(gòu)型的大數(shù)據(jù)量TLV文件解析程序設(shè)計(jì)。在實(shí)際應(yīng)用中,該程序針對內(nèi)含十萬條記錄的約50MB的TLV文件,其解析速率可達(dá)到提取單條記錄平均耗時(shí)在毫秒級,可以滿足快速解析大數(shù)據(jù)量TLV文件以便進(jìn)一步作數(shù)據(jù)正確性校驗(yàn)的需求。
關(guān)鍵詞:TLV文件;快速解析;內(nèi)存映射文件;面向?qū)ο蠹夹g(shù);Java編程
中圖分類號:TP311? ? ? 文獻(xiàn)標(biāo)識碼:A
文章編號:1009-3044(2019)19-0016-04
Abstract: From the actual requirements of a product test, in order to achieve the rapid automatic verification for the data correctness of the large amount of data TLV file, this paper using Java programming language, based on object-oriented technology and memory mapping technology, designed a program for parsing TLV file including a large amount of data with a nested structure. In practical application, for a TLV file with the size of 50MB and containing one hundred thousand records, the parsing rate of the program can reach the extraction of a single record average time in milliseconds. It can satisfy the need of quickly analyzing large amount of data TLV files for further data correctness verification.
Key words: TLV format file; fast parsing; memory-mapped file; object-oriented technology; java programming
抽象語法標(biāo)記[1](ASN.1)是一種ISO/ITU-T標(biāo)準(zhǔn),描述了一種對數(shù)據(jù)進(jìn)行表示、編碼、傳輸和解碼的數(shù)據(jù)格式。TLV文件是指采用標(biāo)準(zhǔn)化編碼規(guī)則[2](如基本編碼規(guī)則,BER)對ASN.1所描述的數(shù)據(jù)進(jìn)行Tag、Length和Value三元組編碼后所形成的數(shù)據(jù)文件。BER編碼有兩種方式。一種是定長編碼方式,由三部分組成,分別對應(yīng)TLV中的Tag、Length、Value。另一種是不定長編碼方式,由四部分組成,比定長編碼多一個(gè)結(jié)束標(biāo)記字段[3]。每種類型都能編碼成定長編碼方式。Tag字段是關(guān)于標(biāo)簽和編碼格式的信息,Length字段定義Value數(shù)值的長度,Value字段表示實(shí)際的數(shù)值。編碼可以是基本型或結(jié)構(gòu)型[4]。如果編碼值的Value字段為基本數(shù)據(jù)類型,則屬基本型。如果編碼值的Value字段中嵌套有TLV結(jié)構(gòu)數(shù)據(jù),則屬結(jié)構(gòu)型。根據(jù)編碼規(guī)則,結(jié)構(gòu)型編碼值的Tag字節(jié)高四位數(shù)值為E,低四位數(shù)值代表嵌套層級。
某產(chǎn)品的合作雙方之間采用TLV文件進(jìn)行數(shù)據(jù)傳輸,單個(gè)TLV文件大小基本在幾十兆字節(jié),數(shù)據(jù)量一般在十萬。在TLV文件提供方完成數(shù)據(jù)文件生成后,進(jìn)行文件傳輸前需對文件內(nèi)數(shù)據(jù)進(jìn)行正確性校驗(yàn)。因?yàn)槲募?shù)據(jù)量比較大,且數(shù)據(jù)本身生成規(guī)則比較復(fù)雜,單純以手工方式進(jìn)行校驗(yàn),完成全部校驗(yàn)耗時(shí)極長。為了實(shí)現(xiàn)對大數(shù)據(jù)量TLV文件數(shù)據(jù)正確性的快速自動校驗(yàn),需要實(shí)現(xiàn)從TLV文件中快速提取各條數(shù)據(jù)。在大數(shù)據(jù)量文件處理方面,內(nèi)存映射文件方法[5]提供了一種高效文件操作途徑?;趦?nèi)存映射文件方法,應(yīng)用程序可以通過內(nèi)存指針對磁盤上的文件進(jìn)行訪問,其過程如同對加載了文件的內(nèi)存進(jìn)行訪問,不必執(zhí)行文件I/O操作,也無需對文件內(nèi)容進(jìn)行緩沖處理。由于取消了將文件數(shù)據(jù)加載到內(nèi)存、數(shù)據(jù)從內(nèi)存到文件的回寫以及釋放內(nèi)存塊等操作,內(nèi)存映射文件方法在處理大數(shù)據(jù)文件時(shí)表現(xiàn)出顯著的速度優(yōu)勢[6-7]。本文基于面向?qū)ο蠹夹g(shù)[8]以及內(nèi)存映射技術(shù),在Eclipse開發(fā)平臺[9]采用Java編程語言,完成了對具有二級嵌套結(jié)構(gòu)型的大數(shù)據(jù)量TLV文件解析程序設(shè)計(jì)。經(jīng)過對內(nèi)含十萬條記錄的49.6MB的TLV文件進(jìn)行實(shí)踐解析,驗(yàn)證了其解析速率可達(dá)到提取單條記錄平均耗時(shí)在毫秒級,基于此實(shí)現(xiàn)了對大批量數(shù)據(jù)正確性的快速校驗(yàn),進(jìn)而完成了對該產(chǎn)品的功能自動化測試。
1 TLV文件數(shù)據(jù)結(jié)構(gòu)實(shí)例簡介
某產(chǎn)品生成的TLV文件內(nèi)含有記錄條目總數(shù)、各記錄數(shù)據(jù)以及其共有數(shù)據(jù),各數(shù)據(jù)以二級嵌套結(jié)構(gòu)型TLV編碼值組成。一級嵌套結(jié)構(gòu)的Tag標(biāo)簽和二級結(jié)構(gòu)嵌套的Tag標(biāo)簽均為一個(gè)字節(jié)表示,該字節(jié)對應(yīng)的十六進(jìn)制字串表示分別記為E1和E2。一級結(jié)構(gòu)的Value值中含記錄條目總數(shù)itemCount、各記錄數(shù)據(jù)的共有數(shù)據(jù)pubData1(可有一條或多條)以及順序排列的各記錄數(shù)據(jù)Item。記錄條目總數(shù)itemCount和共有數(shù)據(jù)pubData1均為基本型TLV編碼數(shù)據(jù),每個(gè)Item記錄數(shù)據(jù)均為一個(gè)結(jié)構(gòu)型TLV編碼數(shù)據(jù),即該TLV文件中的二級結(jié)構(gòu)數(shù)據(jù)。Item記錄數(shù)據(jù)的Value由多個(gè)基本型itemData編碼數(shù)據(jù)組成。二級嵌套結(jié)構(gòu)的TLV文件完整數(shù)據(jù)結(jié)構(gòu)實(shí)例如圖1所示。
Length值采用BER編碼規(guī)則的定長編碼方式。Value值的字節(jié)長度小于等于127時(shí),采用短型長度表示法。Value值的字節(jié)長度大于127時(shí),采用長型長度表示法。短型長度表示法的Length字段由一個(gè)字節(jié)組成,最高位為零,其余位表示長度值。長型長度表示法的Length字段由多個(gè)字節(jié)組成,第一個(gè)字節(jié)最高位為一,其余位表示存放長度所占字節(jié)數(shù),其余字節(jié)存放長度值。
2 大數(shù)據(jù)量TLV文件解析程序設(shè)計(jì)
本文針對特定二級嵌套結(jié)構(gòu)型數(shù)據(jù)文件的TLV解析程序,主要由五個(gè)類構(gòu)成,分別為TLV公共數(shù)據(jù)類、TLV單記錄數(shù)據(jù)類、TLV解析類、數(shù)據(jù)轉(zhuǎn)換工具類和文件操作工具類。TLV解析程序類圖如圖2所示。
TLV公共數(shù)據(jù)類是實(shí)體類,內(nèi)含記錄總數(shù)屬性、公共數(shù)據(jù)串屬性、公共數(shù)據(jù)串長度屬性以及分別對各屬性進(jìn)行設(shè)置的Setter方法和讀取的Getter方法。TLV公共數(shù)據(jù)類用于存放解析過程中從TLV文件中讀取的記錄總數(shù)和公共數(shù)據(jù)。
TLV單記錄數(shù)據(jù)類是實(shí)體類,內(nèi)含各數(shù)據(jù)串屬性及其長度屬性以及分別對各屬性進(jìn)行設(shè)置和讀取的方法。TLV單記錄數(shù)據(jù)類用于存放解析過程中從TLV文件中讀取的單條記錄下的各數(shù)據(jù)。數(shù)據(jù)串屬性個(gè)數(shù)與TLV單記錄中所含數(shù)據(jù)個(gè)數(shù)保持一致。
TLV解析類內(nèi)含待解析TLV文件名配置屬性、解析后文件存儲目錄屬性、MappedByteBuffer類對象buffer屬性、TLV公共數(shù)據(jù)類對象tlvPubInfo屬性、TLV單記錄數(shù)據(jù)類對象tlvItem屬性、TLV單記錄起始位置索引值tlv_Item_Index屬性、下一個(gè)TLV單記錄起始位置索引值tlv_NextItem_Index屬性、主函數(shù)方法、初始化方法、TLV解析方法、獲取TLV公共數(shù)據(jù)方法、獲取單記錄數(shù)據(jù)方法、根據(jù)Tag索引值獲取TLV中長度和數(shù)值方法、寫TLV公共數(shù)據(jù)到二進(jìn)制文件方法和寫單記錄數(shù)據(jù)到二進(jìn)制文件方法。所有屬性變量均為靜態(tài)成員變量。主函數(shù)方法是整個(gè)解析程序的入口。初始化方法用于對待解析TLV文件名配置屬性和解析后文件存儲目錄屬性進(jìn)行初始化。TLV解析方法內(nèi)部通過實(shí)例化RandomAccessFile類對象并調(diào)用其getChannel方法從待解析TLV文件獲得一個(gè)FileChannel類對象,再調(diào)用FileChannel類對象的map方法將文件映射到內(nèi)存MappedByteBuffer類對象(ByteBuffer類的子類)緩存,通過調(diào)用ByteBuffer類的get方法按位置逐字節(jié)讀取文件數(shù)據(jù)。獲取TLV公共數(shù)據(jù)方法用于從TLV文件中讀取公共數(shù)據(jù)并存于TLV公共數(shù)據(jù)類對象,獲取首個(gè)單記錄Tag位置索引值并存于tlv_Item_Index屬性。獲取單記錄數(shù)據(jù)方法用于根據(jù)tlv_Item_Index屬性值從TLV文件中讀取單個(gè)記錄的數(shù)據(jù)并存于TLV單記錄數(shù)據(jù)類對象,獲取下一個(gè)單記錄Tag位置索引值并存于tlv_NextItem_Index屬性,更新tlv_Item_Index屬性值為tlv_NextItem_Index屬性值。根據(jù)Tag索引值獲取TLV中長度和數(shù)值方法供獲取TLV公共數(shù)據(jù)方法和獲取單記錄數(shù)據(jù)方法調(diào)用,該方法內(nèi)部實(shí)現(xiàn)根據(jù)TLV中Tag字節(jié)所在位置索引值讀取該TLV中Length所占字節(jié)數(shù)、Length數(shù)值和Value數(shù)據(jù)值。寫TLV公共數(shù)據(jù)到文件方法和寫單記錄數(shù)據(jù)到二進(jìn)制文件方法分別用于將TLV公共數(shù)據(jù)類對象數(shù)據(jù)和TLV單記錄數(shù)據(jù)類對象數(shù)據(jù)寫入指定目錄下的二進(jìn)制文件。
文件操作工具類完成解析過程中文件夾創(chuàng)建以及各文件的創(chuàng)建與寫入。文件操作工具類內(nèi)含創(chuàng)建目錄方法和寫二進(jìn)制文件方法。創(chuàng)建目錄方法先判斷目錄是否存在,若不存在則新建,若存在則使用已有目錄。寫二進(jìn)制文件方法實(shí)現(xiàn)將已知長度的二進(jìn)制數(shù)據(jù)寫入給定文件名的文件中。數(shù)據(jù)轉(zhuǎn)換工具類用于完成在解析過程中所需的各數(shù)據(jù)轉(zhuǎn)換。數(shù)據(jù)轉(zhuǎn)換工具類內(nèi)含將ASCII碼十六進(jìn)制字串轉(zhuǎn)為數(shù)據(jù)字串方法、將單字節(jié)轉(zhuǎn)為兩字符的十六進(jìn)制字串方法、將十六進(jìn)制字串轉(zhuǎn)為字節(jié)數(shù)組方法和將字節(jié)數(shù)組轉(zhuǎn)為十六進(jìn)制字串方法。
在完成TLV文件到內(nèi)存對象映射后,根據(jù)文件數(shù)據(jù)二級嵌套結(jié)構(gòu)特點(diǎn),依次逐字節(jié)進(jìn)行解析。從一級嵌套結(jié)構(gòu)中提取公共信息、記錄總數(shù)以及首個(gè)二級嵌套結(jié)構(gòu)Tag索引位置。從首個(gè)二級嵌套結(jié)構(gòu)Tag索引位置開始依次提取單記錄下各數(shù)據(jù)。解析過程中Value數(shù)據(jù)根據(jù)Length字段數(shù)據(jù)特點(diǎn)進(jìn)行提取,具體實(shí)現(xiàn):根據(jù)Tag標(biāo)簽所在索引位置確定Length字段首字節(jié)所在索引位置,根據(jù)Length字段首字節(jié)高四位判斷Value值長度是否大于127。若Value值長度不大于127,則從Length字段所在索引位置的下一個(gè)字節(jié)開始提取指定長度的Value數(shù)據(jù)。若Value長度大于127,則根據(jù)Length首字節(jié)低四位判斷Length字段其余字節(jié)個(gè)數(shù)及對應(yīng)的長度值,然后從Length字段首字節(jié)所在索引位置加長度值所占字節(jié)個(gè)數(shù)后所得索引位置的下一個(gè)字節(jié)開始提取指定長度的Value數(shù)據(jù)。結(jié)構(gòu)型編碼值中,Value值中嵌套一個(gè)或多個(gè)子TLV編碼值的情況下,嵌套的首個(gè)TLV編碼值的Tag標(biāo)簽索引位置為結(jié)構(gòu)型編碼值的Tag標(biāo)簽所在索引位置加其Length字段所占字節(jié)數(shù)后所得位置索引值的下一個(gè)字節(jié)所在位置,非首個(gè)TLV編碼值的Tag標(biāo)簽索引位置與首個(gè)相比,還要再順延首個(gè)TLV編碼值中Value值長度個(gè)數(shù)的字節(jié)。解析過程中創(chuàng)建兩級文件夾目錄,一級文件夾目錄名以TLV文件名命名并存于解析前配置的存儲目錄下,二級文件夾目錄名以單記錄數(shù)據(jù)中具有唯一性標(biāo)識的字段值命名,存于一級文件夾目錄下。有多少條Item記錄,則創(chuàng)建多少個(gè)二級文件夾目錄。一級文件夾下存放用于記錄公共信息pubData1的數(shù)據(jù)文件。二級文件夾目錄下存放該Item記錄下各itemData數(shù)據(jù)文件,一個(gè)itemData對應(yīng)一個(gè)數(shù)據(jù)文件。整個(gè)解析過程業(yè)務(wù)流程圖如圖3、圖4和圖5所示。
3 TLV文件解析程序在實(shí)際產(chǎn)品測試中的應(yīng)用
TLV文件數(shù)據(jù)結(jié)構(gòu)實(shí)例簡介中所提及的產(chǎn)品,其主要功能是按某種規(guī)則生成各種數(shù)據(jù)存于數(shù)據(jù)庫,再從數(shù)據(jù)庫按一定規(guī)則提取相應(yīng)數(shù)據(jù)生成TLV文件,以便于在網(wǎng)絡(luò)上進(jìn)行數(shù)據(jù)傳輸。針對該產(chǎn)品若要實(shí)現(xiàn)各數(shù)據(jù)正確性自動化校驗(yàn),需要完成三個(gè)環(huán)節(jié)的自動化代碼設(shè)計(jì),這三個(gè)環(huán)節(jié)按順序分別是檢查數(shù)據(jù)庫中各數(shù)據(jù)是否正確、解析TLV文件提取各數(shù)據(jù)和對TLV中各數(shù)據(jù)與數(shù)據(jù)庫中各數(shù)據(jù)作一致性對比。
本文的TLV解析程序即對應(yīng)第二個(gè)環(huán)節(jié)。執(zhí)行該TLV解析程序,對內(nèi)含十萬條記錄的49.6 MB的TLV文件進(jìn)行解析,整個(gè)解析過程耗時(shí)約為16分鐘,即提取單條長度為521字節(jié)的記錄數(shù)據(jù)平均耗時(shí)為9.6毫秒。解析耗時(shí)少,速度快。
將該解析程序集成到產(chǎn)品功能自動化測試腳本中,即在對數(shù)據(jù)庫各數(shù)據(jù)正確性作比對后,通過實(shí)例化TLV解析類對象并調(diào)用對象的TLV解析方法進(jìn)行TLV文件解析,最后分別讀取解析后存于磁盤中的各數(shù)據(jù)文件中數(shù)據(jù)與數(shù)據(jù)庫中對應(yīng)數(shù)據(jù)作一致性對比,從而實(shí)現(xiàn)整個(gè)數(shù)據(jù)正確性校驗(yàn)過程的全自動化執(zhí)行。解析后所得的各數(shù)據(jù)可全部完成對比校驗(yàn),且校驗(yàn)結(jié)果準(zhǔn)確率為百分百。
該解析程序是基于已知二級嵌套結(jié)構(gòu)型TLV文件數(shù)據(jù)結(jié)構(gòu)定制開發(fā)的,暫不具有普適性。若要使其具有一定的通用性,可在此基礎(chǔ)上作擴(kuò)展改進(jìn)。比如,若待解析TLV文件中數(shù)據(jù)不確定是否有嵌套結(jié)構(gòu)型,或不知有幾層嵌套結(jié)構(gòu),可根據(jù)每個(gè)TLV編碼值的Tag字節(jié)的高四位是否為E判斷其是否為嵌套結(jié)構(gòu)型,然后參照本文解析程序業(yè)務(wù)處理流程中所提及的關(guān)于Length字段的判斷等作后續(xù)分析。針對嵌套結(jié)構(gòu)型TLV編碼值,繼續(xù)根據(jù)其Value字段中的Tag判斷是否有次級嵌套結(jié)構(gòu)型,依次類推,最終判斷出嵌套層級。每增加一個(gè)嵌套結(jié)構(gòu),則按嵌套關(guān)系增加一級對應(yīng)的文件夾目錄,用于存儲該嵌套結(jié)構(gòu)下的各數(shù)據(jù)字段。對于在同一嵌套層級結(jié)構(gòu)下不確定有多少個(gè)TLV編碼值的情況,可采用基于cglib動態(tài)創(chuàng)建類及動態(tài)添加類成員屬性的方式[10],針對該層級在程序執(zhí)行過程中動態(tài)創(chuàng)建對應(yīng)的類及屬性。
4 結(jié)語
本文從某產(chǎn)品測試實(shí)際需求出發(fā),為了實(shí)現(xiàn)對大數(shù)據(jù)量TLV文件數(shù)據(jù)正確性的快速自動校驗(yàn),采用Java編程語言,基于面向?qū)ο蠹夹g(shù)以及內(nèi)存映射技術(shù),完成了對具有二級嵌套結(jié)構(gòu)型的大數(shù)據(jù)量TLV文件解析程序設(shè)計(jì),并將該程序集成到產(chǎn)品功能自動化測試中,完成了大批量數(shù)據(jù)的快速校驗(yàn),校驗(yàn)耗時(shí)少,速率快。
參考文獻(xiàn):
[1] International Telecommunications Union. ISO/IEC 8824-1, Information technology—Abstract Syntax Notation One (ASN.1): Specification of basic notation [S]. Geneva, Switzerland: ITU-T, 2015.
[2] International Telecommunications Union. ISO/IEC 8825-1, Information technology—ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER) [S]. Geneva, Switzerland: ITU-T, 2015.
[3] 娜仁. 一種嵌套數(shù)據(jù)格式的描述文法及其解析工具的設(shè)計(jì)與實(shí)現(xiàn) [D]. 北京郵電大學(xué),2018.
[4] 王沁,許娜,張燕,等. 優(yōu)化TLV編碼規(guī)則[J]. 計(jì)算機(jī)科學(xué),2008,35(11):104-261.
[5] Cay S. Horstmann. Java核心技術(shù) 卷I:基礎(chǔ)知識 [M]. 周立新,陳波,等譯. 10版. 北京:機(jī)械工業(yè)出版社,2016.
[6] 楊寧學(xué),諸昌鈐,聶愛麗. 內(nèi)存映射文件及其在大數(shù)據(jù)量文件快速存取中的應(yīng)用 [J]. 計(jì)算機(jī)應(yīng)用研究,2004(8):187-188.
[7] 于慧彬,齊鵬,梁捷,等. 內(nèi)存映射文件在大數(shù)據(jù)量海洋調(diào)查數(shù)據(jù)處理中的應(yīng)用 [J]. 海洋技術(shù),2010,29(1):32-35.
[8] 丁任霜. 面向?qū)ο笤O(shè)計(jì)與應(yīng)用[M]. 1版. 北京:北京大學(xué)出版社,2011.
[9] Eclipse Foundation. Eclipse [EB/OL]. [2006-6-2]. https://help.eclipse.org/2019-03/index.jsp
[10] 孫藝博,陳英,高晨紅. 基于CGLIB的AOP動態(tài)實(shí)現(xiàn)機(jī)制[J]. 計(jì)算機(jī)工程與設(shè)計(jì),2005,26(11):3118-3120.
【通聯(lián)編輯:梁書】