Jsoup代碼解讀之五-parser(中)

上一篇文章講到了狀態機和詞法分析的基本知識,這一節咱們來分析Jsoup是如何進行詞法分析的。html

代碼結構

先介紹如下parser包裏的主要類:java

  • Parsergit

    Jsoup parser的入口facade,封裝了經常使用的parse靜態方法。能夠設置maxErrors,用於收集錯誤記錄,默認是0,即不收集。與之相關的類有ParseError,ParseErrorList。基於這個功能,我寫了一個PageErrorChecker來對頁面作語法檢查,並輸出語法錯誤。github

  • Tokenui

    保存單個的詞法分析結果。Token是一個抽象類,它的實現有Doctype,StartTag,EndTag,Comment,Character,EOF6種,對應6種詞法類型。code

  • Tokeniserhtm

    保存詞法分析過程的狀態及結果。比較重要的兩個字段是stateemitPending,前者保存狀態,後者保存輸出。其次還有tagPending/doctypePending/commentPending,保存尚未填充完整的Token。blog

  • CharacterReadertoken

    對讀取字符的邏輯的封裝,用於Tokenize時候的字符輸入。CharacterReader包含了相似NIO裏ByteBuffer的consume()unconsume()mark()rewindToMark(),還有高級的consumeTo()這樣的用法。ip

  • TokeniserState

    用枚舉實現的詞法分析狀態機。

  • HtmlTreeBuilder

    語法分析,經過token構建DOM樹的類。

  • HtmlTreeBuilderState

    語法分析狀態機。

  • TokenQueue

    雖然披了個Token的馬甲,實際上是在query的時候用到,留到select部分再講。

詞法分析狀態機

如今咱們來說講HTML的詞法分析過程。這裏借用一下http://ued.ctrip.com/blog/?p=3295裏的圖,圖中描述了一個Tag標籤的狀態轉移過程,

lexer

這裏忽略了HTML註釋、實體以及屬性,只保留基本的開始/結束標籤,例以下面的HTML:

<!-- lang: html -->
<div>test</div>

Jsoup裏詞法分析比較複雜,我從裏面抽取出了對應的部分,就成了咱們的miniSoupLexer(這裏省略了部分代碼,完整代碼能夠看這裏MiniSoupTokeniserState):

<!-- lang: java -->
enum MiniSoupTokeniserState implements ITokeniserState {
    /**
     * 什麼層級都沒有的狀態
     * ⬇
     * <div>test</div>
     *      ⬇
     * <div>test</div>
     */
    Data {
        // in data state, gather characters until a character reference or tag is found
        public void read(Tokeniser t, CharacterReader r) {
            switch (r.current()) {
                case '<':
                    t.advanceTransition(TagOpen);
                    break;
                case eof:
                    t.emit(new Token.EOF());
                    break;
                default:
                    String data = r.consumeToAny('&', '<', nullChar);
                    t.emit(data);
                    break;
            }
        }
    },
    /**
     * ⬇
     * <div>test</div>
     */
    TagOpen {
        ...
    },
    /**
     *           ⬇
     * <div>test</div>
     */
    EndTagOpen {
        ...
    },
    /**
     *  ⬇
     * <div>test</div>
     */
    TagName {
        ...
    };

}

參考這個程序,能夠看到Jsoup的詞法分析的大體思路。分析器自己的編寫是比較繁瑣的過程,涉及屬性值(區分單雙引號)、DocType、註釋、HTML實體,以及一些錯誤狀況。不過了解了其思路,代碼實現也是循序漸進的過程。

下一節開始介紹語法分析部分。

最後仍是附上個人Jsoup解讀系列文章及代碼地址:

https://github.com/code4craft/jsoup-learning

相關文章
相關標籤/搜索