博客 / 詳情

返回

關於字符串解析的一點理解 (以解析一段HTML字符串為例)

在分析一段字符串的時候,可以藉助字符串的indexOf等方法,或者是正則表達式,可是,如果需要解析的是下面這段字符串:

<group>
    <arc c-bind:cx='_width*0.5' c-bind:cy='_height*0.5' c-bind:radius1='_min*0.5' radius2='100' deg='120deg'>
    </arc>
    <group c-for='value in dataList'>
        <circle c-bind:cx='value' c-on:click='doit0' c-bind:cy='value' radius='10'></circle>
        <text c-bind:x='value+20' c-bind:y='value' c-bind:content='value' fill-color='red'></text>
    </group>
    <path>
        <move-to x='10' y='10'></move-to>
        <line-to x='100' y='100'></line-to>
    </path>
    <text c-bind:x='_width*0.5' c-bind:y='_height*0.5'>文字</text>
</group>

什麼時候可能會需要解析類似這樣的字符串?比如你可能希望的nodejs環境開發一個爬蟲,分析爬到的頁面內容,或者是像上面的設計,用html來表達希望繪製什麼樣的圖形後通過js在canvas畫布上繪製出用户的意圖等。

下面,我們來一起看看,具體的怎麼一步步分析處理上面的字符串包含的信息的。

我們把分析分為這幾個步驟: 分析出符號 → 分析出單詞 → 單詞信息分析 → 獲取整體信息

## 分析出符號

我們把一個最小的類似"語句"的稱為單詞(和編譯原理中的單詞加以區分),比如這裏的一個標籤,而為了得到單詞,首先需要分析的稱為符號。比如對 "<div>你好</div>"而言,就存在三個符號: "<div>"、"你好"和"</div>"。

因此,符號就是容易分析出且在此基礎上很容易分析出單詞的存在,具體什麼是符號,取決於分析的內容和目標。

上面的內容,分析出符號的最終結果就是:

比如第二個符號,原始代碼是"<arc c-bind:cx='_width*0.5' c-bind:cy='_height*0.5' c-bind:radius1='_min*0.5' radius2='100' deg='120deg'>",經過分析得到,他是一個標籤的開始部分,名稱叫arc,有一些屬性等。

那麼,這樣的符號是如何分析出來的?很簡單,通過while循環即可。

在分析一個符號開始前,如果遇到的第一個非空白字符是"<",説明這是一個標籤(可能是開始、結束或自閉合的),直到遇到">"的時候,分析結束,也就是獲取了一個符號。

而如果在分析一個符號開始前,遇到的第一個非空白字符不是"<",説明這是一段文本,等遇到"<"的時候,回退一步即可獲得一段文本符號。

而對於標籤符號,只要在分析的時候額外加些判斷,就可以獲取更豐富的信息並獲取屬性值等。從而,就得到了上面的符號列表。

## 分析出單詞

在上面符號列表的基礎上,我們接下來將分析出下面的單詞列表:

很明顯,單詞明顯比符號要少。那麼,如何通過符號列表獲取單詞列表?這會比上一步容易的多。

首先,你讀取了符號列表的第一個符號,如果是文本或者自閉合標籤,就已經獲取了第一個單詞,否則,一定是開始標籤。

現在,你把這個開始標籤存放到棧(先進後出)中去,並佔據單詞列表的一個位置,接着觀察下一個符號,如果是文本或者自閉合標籤,就是一個新的單詞,否則查看是否是開始標籤,如果是,同樣的處理,否則,判斷是否和當前棧頂的元素匹配,如果匹配,出棧並完成了一個單詞的匹配,不然就拋出錯誤。

如此反覆下去,直到符號列表遍歷完畢,觀察此時的棧,如果還有元素,可以默認自閉合即可。

## 單詞信息分析

在上一步,除了分析出單詞外,還需要額外給每個單詞標記層次(例如:根group是第一層,根的孩子是第二層,以此類推)。

層次是如何獲取的?比如,你當前的層次是deep,如果遇到的下一個符號是開始符號,那麼,下一個單詞(或文本,下同)的層次就一個是deep+1,如果遇到的是結束符號,下一個單詞就應該是deep-1,否則就依舊是deep。

獲取整體信息

對於一個字符串表達式而言,就是求值,對於json字符串而言,就是獲取json對象,而在此處,就是要獲取一個帶有關係的DOM樹。

先看看最終的結果:

我們拿節點"<circle c-bind:cx='value' c-on:click='doit0' c-bind:cy='value' radius='10'></circle>"舉例子。

通過parentNode和childNodes指明瞭它的父節點是第二個節點group,沒有孩子,前一個兄弟preNode為null沒有,後一個兄弟nextNode為4,也就是text標籤。

同樣的,我們來説説思路。

在單詞列表中,拿出一個單詞(其實也就是節點或文本節點),如果下一個單詞的deep和當前的一樣,就是兄弟關係,如果小一級,就是當前結點的孩子,這都比較容易。

如果比當前結點的deep大,怎麼辦?説明當前這個節點是葉子,需要回溯,回溯到deep和新的節點deep一樣的即可,那麼新的節點就是此節點的下一個兄弟,以此類推即可。

小結

上述關於解析字符串html的算法,已經封裝並對外開源: 解析xhtml為json對象

user avatar tigerandflower 頭像 dujing_5b7edb9db0b1c 頭像 yilezhiming 頭像 niumingxin 頭像 mrqueue 頭像 yiiouo 頭像 tofrankie 頭像 wupengyu_55d86cdb45293 頭像 fyuanlove 頭像 mmmy_a 頭像 heptagon 頭像 clearlove07 頭像
22 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.