首页  ·  知识 ·  编程语言
文本分析的三种典型设计模式
佚名  本站原创  .NET  编辑:dezai  图片来源:网络
件驱动:Parse-Handler模型(如:xml之SAX模型) 该模型主要有Parser和Handler两个组件。其原型大体如下:
件驱动:Parse-Handler模型(如:xml之SAX模型)

该模型主要有Parser和Handler两个组件。其原型大体如下:

class xxxHandler
{
public
:
   
// any event sended from Parser

   ...
};

class
 xxxParser
{
public
:
   xxxxParser(InputSource
*
 source);
 
   HRESULT parse(xxxxHandler
*
 handler)
   {
      
// analyze source and send event to handler

      ...
   }
};

该模型不规定Handler类型的详细规格,由Parser的实现者根据具体情况而定。
这种模型的核心思想就是由Parser类来具体分析文本的格式,而让信息真正的处理者Handler类从具体的格式中脱离出来,不再需要关心文本物理组织细节。

Tokenizer模型(如:编译器的词法分析器)

这种模型仅涉及一个Tokenizer组件。该组件负责将文本分解为一个个token。其原型大体如下:

class xxxTokenizer
{
public
:
   xxxTokenizer(InputSource
*
 source);
 
   
//

   
// 成功返回S_OK,如果遇到eof返回S_FALSE。
   
//
   HRESULT next(TOKEN*
 token);
};

其中分析的结果以一个结构体TOKEN表示。这个结构体如何设计,同样视具体情况而定。通常它看起来是这样的:

struct TOKEN
{
 UINT type;
 union
 {
  DATATYPE1 data1; 
// 当type = type1时

  DATATYPE2 data2; // 当type = type2时
  ...
 };
};

有了Tokenizer,我们就可以轻易的遍历整个文档:

void visit(InputSource* source)
{
 TOKEN token;
 xxxTokenizer tokenizer(source);
 
while (tokenizer.next(&token) ==
 S_OK)
 {
  print(token);
 }
}

token应当如何划分,其粒度如何,完全取决于设计者的考量。以以下一段xml文本为例:
 content
你可以划分为:

 <elem    // element start
 attr="value"  // attr-value pair
 content    // content
 elem>    // element end

也可以将attr-value pair细分为三个token:attr, assign-symbol, value。
你甚至也可以将整个element作为一个token。

从广义上来说,我们文件系统提供的字节流本身已经是一个Tokenizer了,只不过它划分的token是一个个并无多少逻辑含义的character。

而我们后面提到的DOM模型,也可以算是一个Tokenizer。只不过它划分的token只有一个,就是DOM树,与文件系统的字节流走的是另一个极端。

Tokenizer方式与Parse-Handler方式设计思路,最大的不同在于具体处理信息的人主被动地位相异。在Tokenizer模式下,信息处理者调用Tokenizer得到分析数据,如果相邻的token存在上下文关系,你可以根据需要去取得下一个token,故处于主动地位。

而Parse-Handler模式相关死板一些,一方面Handler类实现者才是真正试图处理信息的人,但是实际上对信息的划分(token)却是由Parse规定的,未必完全符合Handler类的需求。另一方面在token存在上下文关系,当前接受的数据信息不足时,Handler类无法随心所欲的取得下一个token(因为从流程上它是被动的数据接受方),而只能暂时缓存数据,等待下一条信息的到来。

文档对象:DOM模型

DOM模型是最高级的一种模型。它的思路是将文档完整地读入内存,并提供数据访问接口。
DOM模型消耗的内存最多,可提供的服务(我们可以联想一下xml的诸多应用,如xslt等)也最为完整。

这里提到DOM模型消耗的内存最多,这种说法并不全面。例如,在将它与Parse-Handler模型相比时,我们只是计算了Parser的开销,而Handler类是客户实现的,内存开销多少,无从计算。

另一方面,由于DOM模型可以按自己的方式组织数据,它在内存开销上的可优化余地很大,并且客户在使用它时通常不再需要大量的内存分配操作;而Parse-Handler模型中,Handler类的实现者出现蹩脚的设计可能性非常高,计入Handler类的内存开销的话,有时甚至可能远远超过采用DOM模型。

因此我个人认为相对于DOM的能力而言,内存问题在DOM模型中并不算一个了不起的缺陷。实现者可以有很多技巧来进行内存优化。

但是DOM模型有一个问题,就是它一开始就将文档完整的读入了内存,使得它无法胜任那些对响应时间要求较高、希望能够渐进处理的应用。而这一点是采用Parse-Handler模型和Tokenizer模型的好处。

后记

这篇文章写得比较早,因为最近写WINX可视化开发工具相关的设计稿时用到,所以整理了下。我个人在文本文件和各种文档格式的文件打交道较多,多年来也算是形成了一定的经验。我个人现在越来越倾向于采用DOM模型来处理文件。原因在于采用DOM模型有很多优点:

  • DOM模型是提供了最高级的服务,模块的客户负担少。
      
  • 模块划分极其清晰,方便维护。通常DOM模型的内部仍然建立于SAX模型(或Tokenizer模型)上,但是这种依赖局限在DOM模型的内部。因此,程序通常会划分为3层:
       SAX(或Tokenizer) ==> DOM模型 ==> DOMClient(实际的应用)
      
  • 内存管理方面的可优化余地大。在多数情况下,我们建立的DOM模型是只读的(或允许进行少量修改),这种情形下,内存管理方案可以以最简洁的方式实现。下文我们详细讨论这一点。在此之前,我推荐你回顾一下《C++内存管理变革:最袖珍的垃圾回收器》。
      
  • 易获得更好的性能。虽然理论上来讲程序建立在SAX模型性能上可以获得更好的性能,但是经验表明,在团代开发的情形下,采用DOM模型的性能通常可优于建立在SAX模型之上的同样功能的复杂程序(不是简单打印或提取有限数据的情形)。 
本文作者:佚名 来源:本站原创
CIO之家 www.ciozj.com 微信公众号:imciow
    >>频道首页  >>网站首页   纠错  >>投诉
版权声明:CIO之家尊重行业规范,每篇文章都注明有明确的作者和来源;CIO之家的原创文章,请转载时务必注明文章作者和来源;
延伸阅读
也许感兴趣的
我们推荐的
主题最新
看看其它的