SAX 实现“增量文件”

原创 2003年04月01日 09:03:00

SAX 实现“增量文件”

(wang hailong)

本文假设您对XMLDOMSAX等“词儿”比较熟悉。J

本文所举的例子都用Java语言,但原理相通,同样适用于C++C#

1.XML格式的“增量文件”

我们知道,编程打开文件的方式有几种,readwriteappend,等等。Append方式打开的文件就可以称为“增量”文件,新的内容可以被增加到文件的末尾。Log日志文件的纪录,基本都是采取这种方式。“增量文件”的一种重要应用就是Log日志文件。我们不用考虑文件里面“已有”的内容,只要简单地添加,添加。

对于复杂的情况,Append方式就不够用了。如果我们在Log日志文件用到了格式处理,(HTML格式,XML格式),这时,就不能把新的内容简单地添加到文件的末尾,我们必须考虑文件里面“已有”的内容,把新的内容放在合适的位置。比如,Log日志文件采用HTML格式,我们至少需要把新的内容放在放在<html><body></body></html>标签的中间,甚至应该放在<table></table>等标签的中间。我们要保证文件格式的正确,信息位置的正确。

本文提到的格式信息,结构信息,特指XML格式。文中讨论的“增量文件” 特指XML格式的“增量文件”。本文旨在说明解析、操作XML格式“增量文件”的方法。

2.XML数据的处理方法

21  XML Database

如果你需要对文件数据进行功能完善的管理,添加,删除,查询,比较等等,请考虑使用XML Database,请参考一些Open Source Project,比如Apache XIndice

22  DOM + XPath

解析文件,生成DOM Tree,对DOM Tree的节点进行添加操作。可以和XPath进行配合使用,比如,想一步定位到目标节点,”HTML/BODY//TABLE[position()=last()]”,定位到HTML文件的最后一个table,把需要添加的栏目加到这个table中。

关于XPath的使用,可以参考Apache Xalan的例子,关于XPathAPI的例子。

优点:概念清晰,结构良好,代码易读。

缺点:占用较多的时间和空间,使用XPath反复搜索DOM Tree的情况尤其如此。

23  SAX + Filter<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

解析文件,产生SAX事件,处理SAX事件。SAXPipeline管道方式运作,可以在Pipeline管道上加入一些Filter,进行链式处理,前一步SAX事件的处理结果,成为下一步的SAX事件。

缺点:SAX的概念不如DOM直观,代码的易读性不如DOM,结果只能一遍生成,不能反复搜索。SAX编程的结构性通常不如DOM

优点:SAX的编程难度并不比DOM高,需要的编码量甚至常常少于DOM的编码量。SAX快速,占用空间很少。

24  XML-Object Binding

XML数据和Java对象进行绑定。把XML元素映射为一个Java对象,XML元素的属性或子元素映射为Java对象的成员变量。

相关的Open Source Project : Sun Jaxb; Castor. 等等。

绑定方式分为静态绑定和动态绑定。动态绑定比较灵活,但速度比较慢。本文倾向于静态绑定,后面主要讲静态绑定的特性。

静态绑定的过程:编写XML Schema文件;用代码生成工具处理XML Schema文件,自动生成java类;使用自动生成的java类的对象,相当于直接操作XML数据。

优点:这种方法的编程难度最小,代码的结构化最好。速度比DOM快(快的有限),空间比DOM小(小的有限),和DOM一样,生成结果树,能够反复搜索查找。

缺点:速度比SAX慢,占空间比SAX大。当文档结构复杂到一定程度,自动生成的代码成为一个负担。

25  关于Apache Project的其它参考

http://xml.apache.org  核心为 Apache XercesApache Xalan.

http://jakarta.apache.org/commons/digester.html  (small size) Digester : XML-Object动态绑定。

http://jakarta.apache.org/commons/jxpath/index.html  JXPath : 使用XPath操作Java层级对象。

3.SAX实现“增量文件”

现在切入正题。SAX的时间和空间效率最高,本文主要讨论这种方法。

SAX的核心接口是ContentHandler接口,ContentHandler接受SAX事件,进行处理。

我们现在面对的问题是文件操作,首先需要的是一个能够把SAX事件写到文件里面的ContentHandler。这个类对于我们的问题非常重要。

Apache XercesApache Xalan提供了一些XMLSerializer类,提供了一些XMLFilter类,还实现了transform包里面的SourceResult接口。合理地把这些类组装成起来,就可以实现一个灵活的处理管道。

因为本文对应的问题很小,不必要引入这样的复杂度。ContentHandler接口并不是很复杂,而且有这么多的开放源代码可以参考,我们自己来实现一个最简单的SAXWriter类,用来把SAX事件写到一个文件里面。下面的代码是从Apache Xerces的代码中抽取出来,实现最简化的功能。

31  SAXWriter的代码

package example;

import org.xml.sax.SAXException;

import org.xml.sax.Attributes;

import org.xml.sax.ContentHandler;

import org.xml.sax.helpers.DefaultHandler;

import java.io.OutputStreamWriter;

import java.io.PrintWriter;

import java.io.Writer;

import java.io.OutputStream;

import java.io.IOException;

import java.io.UnsupportedEncodingException;

 

public class SAXWriter extends DefaultHandler{

  public SAXWriter() {

  }

  /** Print writer. */

  protected PrintWriter printWriter;

  /** Sets the output stream for printing. */

  public void setOutput(OutputStream stream, String encoding)

 throws UnsupportedEncodingException {

    if (encoding == null) {

      encoding = "UTF8";

    }

    java.io.Writer writer = new OutputStreamWriter(stream, encoding);

    printWriter = new PrintWriter(writer);

  } // setOutput(OutputStream,String)

  /** Sets the output writer. */

  public void setOutput(java.io.Writer writer) {

 

    printWriter = writer instanceof PrintWriter

        ? (PrintWriter) writer : new PrintWriter(writer);

  } // setOutput(java.io.Writer)

 

  // ContentHandler methods

  /** Start element. */

  public void startElement(String uri, String local, String raw,

                           Attributes attrs) throws SAXException {

    printWriter.print('<');

    printWriter.print(raw);

    if (attrs != null) {

      int len = attrs.getLength();

      for (int i = 0; i < len; i++) {

        printWriter.print(' ');

        printWriter.print(attrs.getQName(i));

        printWriter.print("=/"");

        printWriter.print(attrs.getValue(i));

        printWriter.print('"');

      }

    }

    printWriter.print('>');

    printWriter.flush();

  } // startElement(String,String,String,Attributes)

  /** End element. */

  public void endElement(String uri, String local, String raw) throws

      SAXException {

    printWriter.print("</");

    printWriter.print(raw);

    printWriter.print('>');

    printWriter.flush();

  } // endElement(String)

  /** Characters. */

  public void characters(char ch[], int start, int length) throws SAXException {

    printWriter.write(ch, start, length);

    printWriter.flush();

  } // characters(char[],int,int);

  /** Ignorable whitespace. */

  public void ignorableWhitespace(char ch[], int start, int length) throws

      SAXException {

    characters(ch, start, length);

    printWriter.flush();

  } // ignorableWhitespace(char[],int,int);

}

 

下面扩展这个类。

32  ContentAppender的源代码

package example;

import org.xml.sax.SAXException;

import org.xml.sax.Attributes;

import org.xml.sax.ContentHandler;

import org.xml.sax.helpers.DefaultHandler;

 

public class ContentAppender extends SAXWriter{

  String root = null;

  String message = null;

  public ContentAppender(){

  }

  public void appendMessage(String message){

    this.message = message;

  }

 

  /** Start element. */

  public void startElement(String uri, String local, String raw, Attributes attrs)

throws SAXException {

    if(root == null){ // record the root element

      root = raw;

    }

    super.startElement(uri, local, raw, attrs);

  }

 

  /** End element. */

  public void endElement(String uri, String local, String raw) throws SAXException {

    if(message != null && raw.equals(root)){ // if it is the end of root element

      super.startElement("", "message", "message", null);

      printWriter.print(message);

      super.endElement("", "message", "message");

      printWriter.println();

    }

    super.endElement(uri, local, raw);

  }

}

 

扩展SAXWriter,我们可以把新添加的内容放置在任何一个元素里面。ContentAppender类把新添加的内容加在根元素的最后一个节点。

33  主函数

package example;

// java

import java.io.File;

import java.io.InputStream;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.FileNotFoundException;

import java.io.UnsupportedEncodingException;

// org.xml

// Imported SAX classes

import org.xml.sax.InputSource;

import org.xml.sax.SAXException;

import org.xml.sax.helpers.ParserAdapter;

import org.xml.sax.helpers.XMLReaderFactory;

import org.xml.sax.helpers.XMLFilterImpl;

import org.xml.sax.XMLReader;

import org.xml.sax.XMLFilter;

import org.xml.sax.ContentHandler;

// Imported Serializer classes

import org.apache.xalan.serialize.Serializer;

import org.apache.xalan.serialize.SerializerFactory;

import org.apache.xalan.templates.OutputProperties;

 

public class IncrementalWriter {

  public void process(String logFile, String newMessage) throws Exception{

    String newFileName = logFile + ".new";

    // Create an XMLReader.

    XMLReader reader =

 XMLReaderFactory.createXMLReader ("org.apache.xerces.parsers.SAXParser") ;

    ContentAppender handler = new ContentAppender();

    FileOutputStream fo = new FileOutputStream(newFileName);

    handler.setOutput(fo, null);

    handler.appendMessage(newMessage);

 

    reader.setContentHandler(handler);

    reader.parse(logFile);

    fo.close();

    // delete old file and rename new file to log file.

    File oldFile = new File(logFile);

    oldFile.delete();

    File newFile = new File(newFileName);

    newFile.renameTo(new File(logFile));

  }

 

  // for example, a.xml is as below

  // <root>

  //   <message>old message</message>

  // <root>

  public static void main(String[] args) throws Exception{

    IncrementalWriter tester = new IncrementalWriter();

    tester.process("a.xml", "hello"); // append “hello” to a.xml file.

  }

}

这三个文件很简单。Jdk1.4下编译通过。执行的结果是,在a.xml文件中,增加一条元素。

4.应用建议

以上讲述了一个最简单的例子,(限于能力,我无法再简化了)。

以上的代码,每写一条信息,就要处理一次文件,这样频繁读写文件的效率很低。

实际的应用中,可以结合其它方法。比如,先把信息存在内存的DOM Tree或者Java Object里面,存放到一定数量的时候,再调用类似上面的代码,把数据一次存放到文件中。

本文推荐一种方法,把XML-Object BindingSAX结合使用。比如,使用jaxbXML-Object binding机制,用XML-Object存放结构简单的部分数据,然后用SAX把这些数据一次一次地存放到结构复杂的XML文件里面的某个指定位置。

当文件变得很大的时候,建议分成几个文件存储。

有可能加快内存回收的代码如下(可以照此释放DOM Tree或者Java Object):

Vector v = new Vector(2000);

// process vector, after that

v = null; // cancel the reference

System.gc(); // suggest JVM to collect unused Object

5.按照XML格式解析HTML

HTML的结构性不够好,所以出现了结构化文档XML。事物总是相互影响的。HTML的不足催生了XMLXML的出现和发展,又反过来影响HTML的处理过程。

下面简单介绍按照XML结构解析HTML页面的方法——用DOM或者SAX解析HTML

51  HTMLBuider

Apache Xerces包含一个org.apache.html.dom包,实现了这些HTML文档元素的接口。其中的HTMLBuider类是入口类,实现了org.xml.sax.DocumentHandler接口,接受SAX事件,生成HTML文档树。

这种方法的关键是找到一个合适的XMLReader,对HTML文档进行解析。很多XML解析器的容忍度很低,大多数HTML文档不是良好结构的XML,所以,经常有一些重要的HTML元素解析不出来。

52  NekoHTML Project

NekoHTML Open Source Project使用Apache XercesXNIXerces Native Interface)接口,对HTML文档进行解析,是一个不错的开源项目。

能够基本上不遗漏地处理每一个HTML元素生成HTML文档树。能够基本上不遗漏地对应每一个HTML元素产生SAX事件。

NekoHTML可以采用前述的两种方法处理HTML文档——(1DOM+XPath,(2SAX+Filter

DOM+XPath的例子很直观,简单易读,容易上手。SAX+Filter比较难以理解,不易上手。

使用SAX+Filter的步骤:

1.建立自己的Filter,实现org.apache.xerces.xni.parser.XMLDocumentFilter接口。

参见org.cyberneko.html.filters包。

2.创建org.cyberneko.html.parsers.SAXParser。调用

   parser.setFeature("http://cyberneko.org/html/features/balance-tags", false); // optional

   XMLDocumentFilter[] filters = new XMLDocumentFilter[]{myFilter, writer}; // create filters

   parser.setProperty("http://cyberneko.org/html/properties/filters", filters); // set filters

   parser.parse(…);

6.总结

XML和相应工具的出现,大大减轻了我们的文档处理的工作量,加快了我们的工程项目的进度。J

 

 

拷贝增量文件

Dim fso, CopyCount Set fso = CreateObject("Scripting.FileSystemObject") CopyCount = CopyCo...
  • tianlianchao1982
  • tianlianchao1982
  • 2014年08月13日 10:22
  • 738

python脚本实现文件夹增量复制

利用os包os.path.walk() 方法递归的遍历路径和文件 # -*- coding: utf-8 -*- """ Created on Tue Mar 08 10:29:52 2016 @au...
  • zhu418766417
  • zhu418766417
  • 2016年03月09日 16:24
  • 826

XML解析api的对比(JAXB vs SAX)

1、http://stackoverflow.com/questions/7709928/jaxb-vs-dom-and-sax 2、http://www.javacodegeeks.com/201...
  • gjf281
  • gjf281
  • 2014年07月21日 20:10
  • 860

shell 备份指定目录下的增量文件

其中xxx.xxx.xxx.xxx为IP地址 #!/bin/bash list=`ls /opt/sftp/ `  time=`date "+%Y%m%d%H%M%S"` today=`dat...
  • wuxu_nanjing
  • wuxu_nanjing
  • 2017年08月18日 10:09
  • 252

SAX解析和生成XML文档

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本人声明。否则将追究法律责任。 作者:永恒の_☆ 地址:http://blog.csdn.net/chenghui03...
  • ch656409110
  • ch656409110
  • 2013年09月24日 22:37
  • 10838

linux下c语言实现tail -f功能---实时读取变化文件中的增量内容

最近由于项目需要,需要对文件中实时新增的数据进行处理,结合tail -f的逻辑,用c语言实现了这一功能,代码如下: #include #include #include #includ...
  • xiweiwei
  • xiweiwei
  • 2014年01月22日 13:40
  • 1701

Linux rsync增量同步方法

linux rsync增量同步方法 可以先使用 rpm -qa |grep rsync 查看rsync是否已经安装 下面说说rsyns的配置过程 一. 配置服务器端 首先编辑 /etc/r...
  • czgxhf1985
  • czgxhf1985
  • 2012年08月06日 17:40
  • 2778

Java生成xml——SAX生成

一、SAX生成xml实例 SaxToXmlDemo.java public class SaxToXmlDemo { public static void main(String[] args)...
  • u011024652
  • u011024652
  • 2016年05月29日 16:26
  • 2211

SAX方式写入XML文件

SAX方式写入XML文件
  • zjs261019
  • zjs261019
  • 2016年11月14日 19:10
  • 324

java解析xml 之SAX 解析方式原理

Java 对xml 数据的解析 HTTP 网络传输中的数据组织方式有三种: 1:HTML 方式 2:XML方式 3:JSON 方式 XML :称为可扩展标记语言,他与HTML 一样,都是通用...
  • WannerWang
  • WannerWang
  • 2016年02月19日 17:13
  • 644
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:SAX 实现“增量文件”
举报原因:
原因补充:

(最多只允许输入30个字)