第七章、epub文件处理 -- 解析 .xhtml文件 (一)
2019獨角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
第七章、epub文件處理?--?解析?.xhtml文件?(一)
?
本章將介紹代碼如何利用ZLTextPlainModel類來分別處理.xhtml文件中的文本信息與標(biāo)簽信息。
本章涉及的核心類是ZLTextPlainModel類、ZLTextWritablePlainModel類、CachedCharStorage類、XHTMLTagAction接口實現(xiàn)類
?.xhtml文件中包含著兩種信息:文本信息與標(biāo)簽信息。我們需要先正確解析出標(biāo)簽信息代表的結(jié)構(gòu),才能正確得將文本信息顯示在屏幕上。
舉個例子:(這個例子是三體1中的文本)
我們需要讓程序知道這里有四種標(biāo)簽(h1標(biāo)簽、h2標(biāo)簽、b標(biāo)簽、p標(biāo)簽),每種標(biāo)簽代表了不同的格式。程序必須正確顯示出不同標(biāo)簽的格式,才能讓用戶看到正常的文本信息。
在正式開始介紹對.xhtml文件中的文本信息與標(biāo)簽信息的處理流程之前,我們有必要先來介紹下流程中涉及的三個核心類:ZLTextWritablePlainModel類、CachedCharStorage類、XHTMLTagAction接口實現(xiàn)類
ZLTextWritablePlainModel類:
ZLTextWritablePlainModel類是ZLTextPlainModel類的子類,這個類中有三個int數(shù)組與一個CachedCharStorage類。
myStartEntryIndices屬性指向的int數(shù)組記錄了每個段落具體在CachedCharStorage類內(nèi)部的哪一個char數(shù)組里面;
myStartEntryOffsets屬性指向的int數(shù)組記錄了每個段落從CachedCharStorage類內(nèi)部char數(shù)組的哪個位置開始;
myParagraphLengths屬性指向的int數(shù)組記錄每個段落在CachedCharStorage類內(nèi)部char數(shù)組中占據(jù)多少長度;
最后,myStorage屬性指向的CachedCharStorage類內(nèi)部的char數(shù)組則是實際存儲文本信息與標(biāo)簽信息的地方
PS:FBReader程序中一組p標(biāo)簽就代表一個段落(Paragraph)。
CachedCharStorage類:
這個類中有兩個重要的屬性:myArray屬性、myBlockSize屬性
myArray屬性指向一個由char數(shù)組組成的ArrayList(char數(shù)組都設(shè)定為軟引用WeakReference,保證了虛擬機會回收這些char數(shù)組,不會占用過多的內(nèi)存)。這些char數(shù)組里面的元素就代表這.xhtml的文本信息與標(biāo)簽信息。
myBlockSize屬性指向一個int。char數(shù)組的長度最長不會超過這個長度(65536),一旦超過這個長度,代碼就會新建一個char數(shù)組,同時就的數(shù)組會被持久化以便以后再用。
XHTMLTagAction接口實現(xiàn)類:
epub文件中有很多標(biāo)簽,不同的標(biāo)簽代表不同的不同的結(jié)構(gòu),所以FBReader也為不同的標(biāo)簽提供了不同的處理類。這些處理類都是XHTMLTagAction接口的實現(xiàn)類。
標(biāo)簽一般都是成對出現(xiàn)的,XHTMLTagAction接口中的兩個方法分別就對應(yīng)了
?
具體哪些類對應(yīng)哪些標(biāo)簽,是由XHTMLReader類中fillTagTable方法定義的。
?
介紹完三個核心類,我們就可以正式開始介紹對.xhtml文件中的文本信息與標(biāo)簽信息的處理流程了。
我們先以處理一個標(biāo)簽對(包含起始標(biāo)簽和結(jié)束標(biāo)簽)的流程為例。在利用for循環(huán)迭代標(biāo)簽對轉(zhuǎn)換成的char數(shù)組的過程中ZLXMLParser類的doIt方法會對以下的節(jié)點調(diào)用XHTMLReader類進行操作
起始標(biāo)簽右邊的“<”:
記錄char數(shù)組的偏移量,調(diào)用ZLXMLReader接口characterDataHandlerFinal方法(XHTMLReader類并未實現(xiàn)該方法,故可以忽略)
起始標(biāo)簽右邊的“>”:
記錄char數(shù)組的偏移量,取出兩次偏移量當(dāng)中的內(nèi)容,得到當(dāng)前標(biāo)簽的標(biāo)簽名。
ZLXMLParser類的processStartTag方法??->??XHTMLReader類的startElementHandler方法??->??XHTMLTagParagraphWithControlAction類的doAtStart方法
結(jié)束標(biāo)簽左邊的“<”:
記錄char數(shù)組的偏移量,取出兩次偏移量當(dāng)中的內(nèi)容,得到標(biāo)簽當(dāng)中的文本信息
XHTMLReader類的characterDataHandler方法??->??BookReader類的addData方法
將標(biāo)簽當(dāng)中的文本信息存儲到BookReader類的myTextBuffer屬性
結(jié)束標(biāo)簽右邊的“>”:
ZLXMLParser類的processEndTag方法??->??XHTMLReader類的endElementHandler方法??->??標(biāo)簽名對應(yīng)XHTMLTagAction接口實現(xiàn)類的doAtEnd方法
將BookReader類中的myTextBuffer屬性
下面我們再以《三體1》中的一段文本作為例子來詳細介紹下這個流程:
H1標(biāo)簽處理流程
起始標(biāo)簽右邊的“<”:
記錄char數(shù)組的偏移量
起始標(biāo)簽右邊的“>”:
記錄char數(shù)組的偏移量,取出兩次偏移量當(dāng)中的內(nèi)容,得到當(dāng)前標(biāo)簽的標(biāo)簽名。
ZLXMLParser類的processStartTag方法??->??XHTMLReader類的startElementHandler方法??->??XHTMLTagParagraphWithControlAction類的doAtStart方法
doAtStart方法
doAtStart方法會調(diào)用了兩個方法BookReader類的pushKind方法與beginParagraph方法
BookReader類的pushKind方法:
這個方法會在myKindStack屬性中添加FBTextKind.H1(31),而其實myKindStack屬性中已經(jīng)有FBTextKind.REGULAR(0),這個屬性是在OEBBookReader類的readBook方法中設(shè)置的。
BookReader類的beginParagraph方法
這個方法調(diào)用了ZLTextWritablePlainModel類的createParagraph方法,然后用for循環(huán)迭代myKindStack屬性并調(diào)用ZLTextWritablePlainModel類的addControl方法
createParagraph方法更新了ZLTextWritablePlainModel類中的三個屬性,以后會依靠這三個屬性在CachedCharStorage類的char數(shù)組中快速定位某一個段落
???????? ?
addControl方法往CachedCharStorage類中的char數(shù)組加入了兩個可以代表標(biāo)簽的常量
????????
????????? ??PS:每次調(diào)用addControl方法都會加入ZLTextParagraph.Entry.CONTROL(3)這個常量,這個常量是一種標(biāo)示。類似的標(biāo)示還有常量ZLTextParagraph.Entry.TEXT(1),我們會在下一章用到這兩種變量。具體這兩個標(biāo)示是如何發(fā)揮作用的,請參考第十章中的內(nèi)容。
結(jié)束標(biāo)簽左邊的“<”:
記錄char數(shù)組的偏移量,取出兩次偏移量當(dāng)中的內(nèi)容,得到標(biāo)簽當(dāng)中的文本信息
XHTMLReader類的characterDataHandler方法,將標(biāo)簽當(dāng)中的文本信息存儲到myTextBuffer屬性
結(jié)束標(biāo)簽右邊的“>”:
ZLXMLParser類的processEndTag方法??->??XHTMLReader類的endElementHandler方法??->??XHTMLTagParagraphWithControlAction類的doAtEnd方法
doAtEnd方法會調(diào)用ZLTextWritablePlainModel類的addText方法,在CachedCharStorage類中的char數(shù)組中加入三種信息:
1、常量ZLTextParagraph.Entry.TEXT(1),這是一種標(biāo)示,類似常量ZLTextParagraph.Entry.CONTROL(3)
2、標(biāo)簽間文本信息的長度
3、標(biāo)簽間的實際文本信息
????
H2標(biāo)簽、P標(biāo)簽與H1標(biāo)簽基本上是一樣,唯一的區(qū)別在于在起始標(biāo)簽右邊的“>”觸發(fā)的addControl方法中加入不同的常量,而這個變量其實是在FBTextKind接口中定義的。
H2標(biāo)簽會加入FBTextKind.REGULAR(0)以及FBTextKind.H2(32),P標(biāo)簽則只會加入FBTextKind.REGULAR(0)。每次加入這些常量的時候都會同時加入作為標(biāo)示的常量ZLTextParagraph.Entry.TEXT(1)。
B標(biāo)簽與其他三個標(biāo)簽不同,這個標(biāo)簽會觸發(fā)兩次addControl方法,只是兩次的參數(shù)不同。
CachedCharStorage類中新增char數(shù)組
這里需要補充下CachedCharStorage類中新增char數(shù)組的流程,我們在介紹CachedCharStorage類的時候,曾經(jīng)講過:“BookModel類中的myBlockSize屬性指向一個int。char數(shù)組的長度最長不會超過這個int(65536),一旦超過這個長度,代碼就會新建一個char數(shù)組,同時就的數(shù)組會被持久化以便以后再用。”
新增char數(shù)組的工作由CachedCharStorage的createNewBlock方法完成
將舊的char數(shù)組持久化的工作是在CachedCharStorage類的freezeLastBlock方法完成的。
在sd卡上的Books/.FBReader這個文件夾里面,我們可以找到這些持久化了的文件。
這個位置是在BookModel的構(gòu)建函數(shù)中用過Paths類獲得的。
其實我們可以嘗試把持久化char數(shù)組的方法改為utf8的編碼,然后在把得到的文件后綴名改成.txt
打開這個txt文件,我們就可以看到下面這樣的數(shù)據(jù)
對比原先的xml文件,那些奇怪的符號就代表了標(biāo)簽信息
好,至此為止,我們將.xhtml文件中的文本信息與標(biāo)簽信息存儲到了ZLTextPlainModel中。而要想讓程序最終用中正確的格式顯示文本信息,還需要配合之后第八章(定位指定段落)以及第九章(顯示.html文件)的內(nèi)容才能讓用戶看到格式正確的文本。
題外話
最后,插幾句題外話,寫一些自己的思考:
FBReader使用char數(shù)組的形式來存儲xml文件內(nèi)容與結(jié)構(gòu)信息,然后依靠記錄每個段落在char數(shù)組中具體位置的int數(shù)組快速獲取指定段落在char數(shù)組中的部分。選擇數(shù)組是ok的,在數(shù)組中定位到某一個部分,速度還是比較快的,但是同時,也因為使用了數(shù)組這個數(shù)據(jù)結(jié)構(gòu),程序也是相對來說比較占用內(nèi)存的。一般來說,電子書都是從一整段數(shù)據(jù)中按順序取出一部分?jǐn)?shù)據(jù)顯示在屏幕上,這種業(yè)務(wù)需求其實用樹的數(shù)據(jù)結(jié)構(gòu)也是非常合適的。而說到樹的數(shù)據(jù)結(jié)構(gòu),其實Android手機中正好有一個現(xiàn)成的sqlite數(shù)據(jù)庫可以提供這種樹的數(shù)據(jù)結(jié)構(gòu)。我們可以試想一下,如果改用sqlite數(shù)據(jù)庫來存儲和檢索xml文件的內(nèi)容與結(jié)構(gòu)信息的話,相比數(shù)組會有怎樣的好處。我想最起碼會有三個好處:第一、節(jié)省手機的內(nèi)存,sqlite數(shù)據(jù)庫做好索引之后,無需像數(shù)組那樣把數(shù)據(jù)全部裝進內(nèi)存后才能檢索。這樣一來,程序就節(jié)省了內(nèi)存;二、方便跨平臺的開發(fā),sqlite支持linux、IOS以及HTML5,如果使用sqlite來作為存儲和檢索xml文件的方式,那么Android程序員、IOS程序員以及pc前端程序員只需要根據(jù)約定的sql語句就能完成開發(fā),而不必獨立開發(fā)三種語言,或者用c或c++另外再開發(fā)一個底層庫;三、方便與服務(wù)器端對接,當(dāng)需要對客戶端用戶的閱讀記錄進行收集與分析的時候,如果客戶端與服務(wù)端使用同一種或相近的sql結(jié)構(gòu)的話,那么對接的難度就會降低很多。
轉(zhuǎn)載于:https://my.oschina.net/u/938986/blog/335919
創(chuàng)作打卡挑戰(zhàn)賽贏取流量/現(xiàn)金/CSDN周邊激勵大獎
總結(jié)
以上是生活随笔為你收集整理的第七章、epub文件处理 -- 解析 .xhtml文件 (一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 牛客题霸 [回文数字(palindrom
- 下一篇: 牛客题霸 [二叉树中是否存在节点和为指定