转载自  Jsoup代码解读之四-parser(上)

作为Java世界最好的HTML 解析库,Jsoup的parser实现非常具有代表性。这部分也是Jsoup最复杂的部分,需要一些数据结构、状态机乃至编译器的知识。好在HTML语法不复杂,解析只是到DOM树为止,所以作为编译器入门倒是挺合适的。这一块不要指望囫囵吞枣,我们还是泡一杯咖啡,细细品味其中的奥妙吧。

基础知识

编译器

将计算机语言转化为另一种计算机语言(通常是更底层的语言,例如机器码、汇编、或者JVM字节码)的过程就叫做编译(compile)。编译器(Compiler)是计算机科学的一个重要领域,已经有很多年历史了,而最近各种通用语言层出不穷,加上跨语言编译的兴起、DSL概念的流行,都让编译器变成了一个很时髦的东西。

编译器领域相关有三本公认的经典书籍,龙书《Compilers: Principles, Techniques, and Tools 》,虎书《Modern Compiler Implementation in X (X表示各种语言)》,鲸书《Advanced Compiler Design and Implementation》。其中龙书是编译理论方面公认的不二之选,而后面两本则对实践更有指导意义。另外@装配脑袋有个很好的编译器入门系列博客:http://www.cnblogs.com/Ninputer/archive/2011/06/07/2074632.html

编译器的基本流程如下:

其中词法分析、语法分析、语义分析这部分又叫编译器的前端(front-end),而此后的中间代码生成直到目标生成、优化等属于编译器的后端(back-end)。编译器的前端技术已经很成熟了,也有yacc这样的工具来自动进行词法、语法分析(Java里也有一个类似的工具ANTLR),而后端技术更加复杂,也是目前编译器研究的重点。

说了这么多,回到咱们的HTML上来。HTML是一种声明式的语言,可以理解它的最终的输出是浏览器里图形化的页面,而并非可执行的目标语言,因此我将这里的Translate改为了Render。

在Jsoup(包括类似的HTML parser)里,只做了Lex(词法分析)、Parse(语法分析)两步,而HTML parse最终产出结果,就是DOM树。至于HTML的语义解析以及渲染,不妨看看携程UED团队的这篇文章:《浏览器是怎样工作的:渲染引擎,HTML解析》。

状态机

Jsoup的词法分析和语法分析都用到了状态机。状态机可以理解为一个特殊的程序模型,例如经常跟我们打交道的正则表达式就是用状态机实现的。

它由状态(state)和转移(transition)两部分构成。根据状态转移的可能性,状态机又分为DFA(确定有限状态机)和NFA(非确定有限状态自动机)。这里拿一个最简单的正则表达式"a[b]*"作为例子,我们先把它映射到一个状态机DFA,大概是这样子:

状态机本身是一个编程模型,这里我们尝试用程序去实现它,那么最直接的方式大概是这样:

<!-- lang: java -->
public void process(StringReader reader) throws StringReader.EOFException {char ch;switch (state) {case Init:ch = reader.read();if (ch == 'a') {state = State.AfterA;accum.append(ch);}break;case AfterA:...break;case AfterB:...break;case Accept:...break;}
}

这样写简单的状态机倒没有问题,但是复杂情况下就有点难受了。还有一种标准的状态机解法,先建立状态转移表,然后使用这个表建立状态机。这个方法的问题就是,只能做纯状态转移,无法在代码级别操作输入输出。

Jsoup里则使用了状态模式来实现状态机,初次看到时,确实让人眼前一亮。状态模式是设计模式的一种,它将状态和对应的行为绑定在一起。而在状态机的实现过程中,使用它来实现状态转移时的处理再合适不过了。

"a[b]*"的例子的状态模式实现如下,这里采用了与Jsoup相同的方式,用到了枚举来实现状态模式:

<!-- lang: java -->
public class StateModelABStateMachine implements ABStateMachine {State state;StringBuilder accum;enum State {Init {@Overridepublic void process(StateModelABStateMachine stateModelABStateMachine, StringReader reader) throws StringReader.EOFException {char ch = reader.read();if (ch == 'a') {stateModelABStateMachine.state = AfterA;stateModelABStateMachine.accum.append(ch);}}},Accept {...},AfterA {...},AfterB {...};public void process(StateModelABStateMachine stateModelABStateMachine, StringReader reader) throws StringReader.EOFException {}}public void process(StringReader reader) throws StringReader.EOFException {state.process(this, reader);}
}

PS:我在github上fork了一份Jsoup的代码,把这系列文章提交了上去,并且给一些代码增加了中文注释,有兴趣的可以看看https://github.com/code4craft/jsoup-learning。本文中提到的几种状态机的完整实现在这个仓库的https://github.com/code4craft/jsoup-learning/tree/master/src/main/java/us/codecraft/learning路径下。

下一篇文章将从Jsoup的词法分析器开始来讲状态机的使用。

Jsoup代码解读之四-parser(上)相关推荐

  1. Jsoup代码解读之五-parser(中)

    转载自    Jsoup代码解读之五-parser(中) 上一篇文章讲到了状态机和词法分析的基本知识,这一节我们来分析Jsoup是如何进行词法分析的. 代码结构 先介绍以下parser包里的主要类: ...

  2. Jsoup代码解读之六-parser(下)

    转载自   Jsoup代码解读之六-parser(下) 最近生活上有点忙,女儿老是半夜不睡,精神状态也不是很好.工作上的事情也谈不上顺心,有很多想法但是没有几个被认可,有些事情也不是说代码写得好就行的 ...

  3. jsoup获得css,Jsoup代码解读之五-实现一个CSS Selector

    Jsoup代码解读之七-实现一个CSS Selector 当当当!终于来到了Jsoup的特色:CSS Selector部分.selector也是我写的爬虫框架webmagic开发的一个重点.附上一张s ...

  4. Jsoup代码解读之一-概述

    转载自   Jsoup代码解读之一-概述 今天看到一个用python写的抽取正文的东东,美滋滋的用Java实现了一番,放到了webmagic里,然后发现Jsoup里已经有了-觉得自己各种不靠谱啊!算了 ...

  5. Jsoup代码解读之二-DOM相关对象

    转载自  Jsoup代码解读之二-DOM相关对象 之前在文章中说到,Jsoup使用了一套自己的DOM对象体系,和Java XML API互不兼容.这样做的好处是从XML的API里解脱出来,使得代码精炼 ...

  6. Jsoup代码解读之三-Document的输出

    转载自   Jsoup代码解读之三-Document的输出 Jsoup官方说明里,一个重要的功能就是***output tidy HTML***.这里我们看看Jsoup是如何输出HTML的. HTML ...

  7. Jsoup代码解读之七-实现一个CSS Selector

    转载自    Jsoup代码解读之七-实现一个CSS Selector 当当当!终于来到了Jsoup的特色:CSS Selector部分.selector也是我写的爬虫框架webmagic开发的一个重 ...

  8. softmax理论及代码解读——UFLDL

    前言 看了各种softmax以后迷迷糊糊的,还是研究一下UFLDL的教程稳点.当然还是得参考挺多教程的:UFLDL-softmax .Softmax的理解与应用 .Logistic 分类器与 soft ...

  9. Lossless Codec---APE代码解读系列(二)

    APE file 一些概念 APE代码解读系列(一) APE代码解读系列(三) 1. 先要了解APE compression level APE主要有5level, 分别是: CompressionL ...

最新文章

  1. 深度学习:人脸识别学习笔记
  2. source Java_JAVA SOURCE (1)
  3. 过程计算机控制综合课程设计,自动化10本 12升《计算机控制综合课程设计》任务书...
  4. 汉堡菜单_汉堡菜单-可访问性和用户体验设计原则的挑战?
  5. P3226-[HNOI2012]集合选数【状压dp】
  6. LeetCode 10. 正则表达式匹配(递归/DP)
  7. bootstrap-table toolbar图标换文字_iPhone 也能随意换字体啦~
  8. 使用 Hapi 开发 RESTful APIs
  9. 幸运福彩3d 是什么
  10. python aic准则_pythonAIC准则下线性回归实现及模型检验案例分析
  11. bench_ios框架的设计思考,基础库、runtime和组件化
  12. vv7无法启动显示发动机故障_启动系统故障引起的发动机无法启动诊断方法
  13. 小小的天有大大的梦想
  14. [ArcGIS] Python计算Geometrical interval自然间断点 | jenkspy库
  15. java将uuid转换成大写,python生成大写32位uuid代码
  16. cv2.imread()和cv2.cvtColor() 的使用
  17. cad批量选择相同块_在CAD中怎样批量选择同类型的构件
  18. 百度搜索引擎 - 搜索关键字排名 API
  19. 基于区块链技术,电子商务平台将提高安全性和透明度
  20. 表单和表单控件以及表单域详解

热门文章

  1. vector容器中重写sort方法
  2. [C++11]函数模板的默认模板参数
  3. fh 幅频特性曲线怎么画fl_初学者怎么练习线条?教你如何画出流畅线条的技巧...
  4. 高级数据结构---优先队列
  5. Spring boot starter
  6. 牛客挑战赛47 D Lots of Edges(最短路+递归枚举子集)
  7. P3899 [湖南集训]谈笑风生
  8. 牛客题霸 [判断一个链表是否为回文结构] C++题解/答案
  9. [学习笔记] 乱世之神杀疯了 —— K-D tree
  10. CF1114F-Please, another Queries on Array?【线段树,欧拉函数】