第七章、epub檔案處理 -- 解析 .xhtml檔案 (一)

來源:互聯網
上載者:User

標籤:android   style   http   io   os   ar   使用   for   strong   

第七章、 epub 檔案處理  --  解析  .xhtml 檔案 (一)

 

本章將介紹代碼如何利用ZLTextPlainModel類來分別處理.xhtml檔案中的文本資訊與標籤資訊。

本章涉及的核心類是ZLTextPlainModel類、ZLTextWritablePlainModel類、CachedCharStorage類、XHTMLTagAction介面實作類別

 .xhtml檔案中包含著兩種資訊:文本資訊與標籤資訊。我們需要先正確解析出標籤資訊代表的結構,才能正確得將文本資訊顯示在螢幕上。

舉個例子:(這個例子是三體1中的文本)


我們需要讓程式知道這裡有四種標籤(h1標籤、h2標籤、b標籤、p標籤),每種標籤代表了不同的格式。程式必須正確顯示出不同標籤的格式,才能讓使用者看到正常的文本資訊。




在正式開始介紹對.xhtml檔案中的文本資訊與標籤資訊的處理流程之前,我們有必要先來介紹下流程中涉及的三個核心類:ZLTextWritablePlainModel類、CachedCharStorage類、XHTMLTagAction介面實作類別

ZLTextWritablePlainModel 類:

ZLTextWritablePlainModel類是ZLTextPlainModel類的子類,這個類中有三個int數組與一個CachedCharStorage類。

myStartEntryIndices屬性指向的int數組記錄了每個段落具體在CachedCharStorage類內部的哪一個char數組裡面;

myStartEntryOffsets屬性指向的int數組記錄了每個段落從CachedCharStorage類內部char數組的哪個位置開始;

myParagraphLengths屬性指向的int數組記錄每個段落在CachedCharStorage類內部char數組中佔據多少長度;

最後,myStorage屬性指向的CachedCharStorage類內部的char數組則是實際儲存文本資訊與標籤資訊的地方

PS:FBReader程式中一組p標籤就代表一個段落(Paragraph)。



CachedCharStorage 類:

這個類中有兩個重要的屬性:myArray屬性、myBlockSize屬性

myArray屬性指向一個由char數組組成的ArrayList(char數組都設定為軟引用WeakReference,保證了虛擬機器會回收這些char數組,不會佔用過多的記憶體)。這些char數組裡面的元素就代表這.xhtml的文本資訊與標籤資訊。

myBlockSize屬性指向一個int。char數組的長度最長不會超過這個長度(65536),一旦超過這個長度,代碼就會建立一個char數組,同時就的數組會被持久化以便以後再用。



XHTMLTagAction 介面實作類別:

epub檔案中有很多標籤,不同的標籤代表不同的不同的結構,所以FBReader也為不同的標籤提供了不同的處理類。這些處理類都是XHTMLTagAction介面的實作類別。

標籤一般都是成對出現的,XHTMLTagAction介面中的兩個方法分別就對應了

 

具體哪些類對應哪些標籤,是由XHTMLReader類中fillTagTable方法定義的。

 


介紹完三個核心類,我們就可以正式開始介紹對.xhtml檔案中的文本資訊與標籤資訊的處理流程了。

我們先以處理一個標籤對(包含起始標籤和結束標籤)的流程為例。在利用for迴圈迭代標籤對轉換成的char數組的過程中ZLXMLParser類的doIt方法會對以下的節點調用XHTMLReader類進行操作

起始標籤右邊的“<”:

記錄char數組的位移量,調用ZLXMLReader介面characterDataHandlerFinal方法(XHTMLReader類並未實現該方法,故可以忽略)

起始標籤右邊的“>”:

記錄char數組的位移量,取出兩次位移量當中的內容,得到當前標籤的標籤名。

ZLXMLParser類的processStartTag方法  ->  XHTMLReader類的startElementHandler方法  ->  XHTMLTagParagraphWithControlAction類的doAtStart方法

結束標籤左邊的“<”:

記錄char數組的位移量,取出兩次位移量當中的內容,得到標籤當中的文本資訊

XHTMLReader類的characterDataHandler方法  ->  BookReader類的addData方法

將標籤當中的文本資訊儲存到BookReader類的myTextBuffer屬性

結束標籤右邊的“>”:

ZLXMLParser類的processEndTag方法  ->  XHTMLReader類的endElementHandler方法  ->  標籤名對應XHTMLTagAction介面實作類別的doAtEnd方法

將BookReader類中的myTextBuffer屬性



下面我們再以《三體1》中的一段文本作為例子來詳細介紹下這個流程:

H1 標籤處理流程 起始標籤右邊的“ < ”:

記錄char數組的位移量


起始標籤右邊的“ > ”:

記錄char數組的位移量,取出兩次位移量當中的內容,得到當前標籤的標籤名。

ZLXMLParser類的processStartTag方法  ->  XHTMLReader類的startElementHandler方法  ->  XHTMLTagParagraphWithControlAction類的doAtStart方法

doAtStart方法

doAtStart方法會調用了兩個方法BookReader類的pushKind方法與beginParagraph方法

BookReader類的pushKind方法:

這個方法會在myKindStack屬性中添加FBTextKind.H1(31),而其實myKindStack屬性中已經有FBTextKind.REGULAR(0),這個屬性是在OEBBookReader類的readBook方法中設定的。

BookReader類的beginParagraph方法

這個方法調用了ZLTextWritablePlainModel類的createParagraph方法,然後用for迴圈迭代myKindStack屬性並調用ZLTextWritablePlainModel類的addControl方法


createParagraph方法更新了ZLTextWritablePlainModel類中的三個屬性,以後會依靠這三個屬性在CachedCharStorage類的char數組中快速定位某一個段落

          

addControl方法往CachedCharStorage類中的char數組加入了兩個可以代表標籤的常量

        

            PS:每次調用addControl方法都會加入ZLTextParagraph.Entry.CONTROL3)這個常量,這個常量是一種標示。類似的標示還有常量ZLTextParagraph.Entry.TEXT1),我們會在下一章用到這兩種變數。具體這兩個標示是如何發揮作用的,請參考第十章中的內容。


結束標籤左邊的“ < ”:

記錄char數組的位移量,取出兩次位移量當中的內容,得到標籤當中的文本資訊

XHTMLReader類的characterDataHandler方法,將標籤當中的文本資訊儲存到myTextBuffer屬性



結束標籤右邊的“ > ”:

ZLXMLParser類的processEndTag方法  ->  XHTMLReader類的endElementHandler方法  ->  XHTMLTagParagraphWithControlAction類的doAtEnd方法

doAtEnd方法會調用ZLTextWritablePlainModel類的addText方法,在CachedCharStorage類中的char數組中加入三種資訊:

1、常量ZLTextParagraph.Entry.TEXT(1),這是一種標示,類似常量ZLTextParagraph.Entry.CONTROL(3)

2、標籤間文本資訊的長度

3、標籤間的實際文本資訊

    


H2標籤、P標籤與H1標籤基本上是一樣,唯一的區別在於在起始標籤右邊的“>”觸發的addControl方法中加入不同的常量,而這個變數其實是在FBTextKind介面中定義的。


H2標籤會加入FBTextKind.REGULAR(0)以及FBTextKind.H2(32),P標籤則只會加入FBTextKind.REGULAR(0)。每次加入這些常量的時候都會同時加入作為標示的常量ZLTextParagraph.Entry.TEXT(1)。

B標籤與其他三個標籤不同,這個標籤會觸發兩次addControl方法,只是兩次的參數不同。



CachedCharStorage 類中新增 char 數組

這裡需要補充下CachedCharStorage類中新增char數組的流程,我們在介紹CachedCharStorage類的時候,曾經講過:“BookModel類中的myBlockSize屬性指向一個int。char數組的長度最長不會超過這個int(65536),一旦超過這個長度,代碼就會建立一個char數組,同時就的數組會被持久化以便以後再用。”

新增char數組的工作由CachedCharStorage的createNewBlock方法完成


將舊的char數組持久化的工作是在CachedCharStorage類的freezeLastBlock方法完成的。


在sd卡上的Books/.FBReader這個檔案夾裡面,我們可以找到這些持久化了的檔案。


這個位置是在BookModel的構建函數中用過Paths類獲得的。



其實我們可以嘗試把持久化char數組的方法改為utf8的編碼,然後在把得到的檔案尾碼名改成.txt


開啟這個txt檔案,我們就可以看到下面這樣的資料


對比原先的xml檔案,那些奇怪的符號就代表了標籤資訊


好,至此為止,我們將.xhtml檔案中的文本資訊與標籤資訊儲存到了ZLTextPlainModel中。而要想讓程式最終用中正確的格式顯示文本資訊,還需要配合之後第八章(定位指定段落)以及第九章(顯示.html檔案)的內容才能讓使用者看到格式正確的文本。


題外話

最後,插幾句題外話,寫一些自己的思考:

FBReader使用char數組的形式來儲存xml檔案內容與結構資訊,然後依靠記錄每個段落在char數組中具體位置的int數組快速擷取指定段落在char數組中的部分。選擇數組是ok的,在數組中定位到某一個部分,速度還是比較快的,但是同時,也因為使用了數組這個資料結構,程式也是相對來說比較佔用記憶體的。一般來說,電子書都是從一整段資料中按順序取出一部分資料顯示在螢幕上,這種業務需求其實用樹的資料結構也是非常合適的。而說到樹的資料結構,其實Android手機中正好有一個現成的sqlite資料庫可以提供這種樹的資料結構。我們可以試想一下,如果改用sqlite資料庫來儲存和檢索xml檔案的內容與結構資訊的話,相比數組會有怎樣的好處。我想最起碼會有三個好處:第一、節省手機的記憶體,sqlite資料庫做好索引之後,無需像數組那樣把資料全部裝進記憶體後才能檢索。這樣一來,程式就節省了記憶體;二、方便跨平台的開發,sqlite支援linux、IOS以及HTML5,如果使用sqlite來作為儲存和檢索xml檔案的方式,那麼Android程式員、IOS程式員以及pc前端程式員只需要根據約定的sql語句就能完成開發,而不必獨立開發三種語言,或者用c或c++另外再開發一個底層庫;三、方便與伺服器端對接,當需要對用戶端使用者的閱讀記錄進行收集與分析的時候,如果用戶端與服務端使用同一種或相近的sql結構的話,那麼對接的難度就會降低很多。


第七章、epub檔案處理 -- 解析 .xhtml檔案 (一)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.