`
moshalanye
  • 浏览: 24826 次
  • 来自: ...
社区版块
存档分类
最新评论

大容量XML文件解析辅助--xml批量分解

阅读更多
   在项目里面遇到了一些被解析的xml文件超过30M 或 60M 以上的情况, 现在已经不好去说为什么不在一开始产生xml的情况下就把xml 做小点,但是遇到这个问题后,我只能解决问题了,解决问题同时害怕重复发明轮子,我也去看了下现有的xml 解析东西,jdom 的SAXBuilder和 dom4j 的SAXReader都是把XML文件一次读入,xml文件过来 会报溢出的异常    但即使SAXParser是可以批量读入解析,但它也是一次解析完,假设XML文件中有一万条数据,解析后就必须在内存中放这么多的对象     个人觉得这样有些不灵活,就自己做了个小东西来切分    但前提是这个xml文件得有文件头 <?xml version="1.0" encoding="GBK"?>   encoding必须跟文件编码格式一致 ,不然解析的时候会出乱码。
   
    个人水平有限,但很希望得到大家的指正,希望大家不吝啬手中的砖头
package searchRing.ring.util.xmlBufferTool;

import java.io.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;


public class XMLBufferTool {
    private static final int defaultLineCount = 10;
    private static final int defaultMaxOutputSize = 50;

    private static final Pattern elementPattern = Pattern.compile("<[a-zA-Z]+>");
    private static final Pattern charSetPattern = Pattern.compile("<[?][[0-9a-zA-Z]|[\\s]|[=]|[\"]|[.]|[-]]+[?]>");

    private StringBuffer xmlContentBuffer;


    /* just used to store and output the data divided */
    XMLOutputBuffer xmlOutput;

    private String charSetTitle = "";

    private String rootElemetMark = "";

    private String childElementMark = "";


    InputStreamReader bufferedReader;
    InputStream fileInputStream;


    public XMLBufferTool(String xmlFilePath) {

        this.xmlContentBuffer = new StringBuffer();

        try {

            this.fileInputStream = new FileInputStream(xmlFilePath);
//             bufferedReader = new InputStreamReader(fileInputStream, "UTF-8");
            String charSet = getCharSet(xmlFilePath);
            if (charSet != null)
                bufferedReader = new InputStreamReader(fileInputStream, charSet);
            else
                bufferedReader = new InputStreamReader(fileInputStream);
        } catch (FileNotFoundException fe) {
            fe.printStackTrace();
        } catch (UnsupportedEncodingException uee) {
            uee.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }


        try {
            preparePaser();
        } catch (IOException ie) {
            ie.printStackTrace();
        }
    }


    public String getCharSetTitle() {
        return charSetTitle;
    }

    public String getRootElemetMark() {
        return rootElemetMark;
    }

    private String getCharSet(String filePath) throws IOException {
        char temp[] = new char[512];
        FileInputStream tempInput = new FileInputStream(filePath);
        InputStreamReader tempReader = new InputStreamReader(tempInput);

        int i = tempReader.read(temp);

        tempReader.close();
        tempInput.close();
        if (i < 0)
            return null;

        String tempStr = new String(temp);
        Matcher m = charSetPattern.matcher(tempStr);
        if (m.find()) {
            String charSetStr = tempStr.substring(m.start(), m.end());
            Pattern tempP = Pattern.compile("[\"][[0-9a-zA-Z]|[-]]+[\"]");
            Matcher tempM = tempP.matcher(charSetStr);
            if (tempM.find()) {
                String charSet = charSetStr.substring(tempM.start(), tempM.end());
                return charSet.substring(1, charSet.length() - 1);
            }
        }

        return null;
    }


    private void preparePaser() throws IOException {
        readSomeLine(defaultLineCount);
        Matcher m = charSetPattern.matcher(xmlContentBuffer);
        if (m.find()) {
            this.charSetTitle = this.xmlContentBuffer.substring(m.start(), m.end());
            this.xmlContentBuffer.delete(0, m.end());
        }

        m = elementPattern.matcher(xmlContentBuffer);
        if (m.find()) {
            this.rootElemetMark = this.xmlContentBuffer.substring(m.start(), m.end());
            this.xmlContentBuffer.delete(0, m.end());
        }

        m = elementPattern.matcher(xmlContentBuffer);
        if (m.find()) {
            this.childElementMark = this.xmlContentBuffer.substring(m.start(), m.end());
        }
        this.xmlOutput = new XMLOutputBuffer(this.childElementMark);

        parserBuffer();
    }


    private int readSomeLine(int lineCount) throws IOException {

        char buffer[] = new char[1024];
        int i = 0;
        int index = 0;
        /* be careful of the sequence of the boolean caculation */
        while (i++ < lineCount && (index = this.bufferedReader.read(buffer)) > 0) {
            xmlContentBuffer.append(buffer, 0, index);
        }

        return index;

    }


    private void parserBuffer() {

        int lastIndex = this.xmlContentBuffer.lastIndexOf(this.childElementMark);

        if (lastIndex > 0) {
            this.xmlOutput.append(this.xmlContentBuffer.substring(0, lastIndex));
            this.xmlContentBuffer.delete(0, lastIndex);
        }
    }

    public StringBuffer popDividedDataAfterParser() throws IOException {

        while (this.xmlOutput.getItemCount() < defaultMaxOutputSize) {
            int i = readSomeLine(defaultLineCount);
            parserBuffer();
            if (i < 0)
                break;
        }

        if (this.xmlOutput.getItemCount() == 0)
            return null;

        StringBuffer returnSB = this.xmlOutput.getXmlOutput();
        this.xmlOutput.clearBuffer();
        return returnSB.insert(0, this.rootElemetMark).append(this.rootElemetMark.replaceFirst("<", "</"));

    }


    public static void main(String args[]) throws Exception {
        String str = "F:/ringInfoXML/ringTime.xml";

        XMLBufferTool xmlb = new XMLBufferTool(str);

        StringBuffer s = xmlb.popDividedDataAfterParser();
        int i = 0;
        Matcher m = Pattern.compile("<ring>").matcher(s);
        while (m.find())
            i++;

        System.out.println(i);
        System.out.println(s);


    }

    private static class XMLOutputBuffer {
        private StringBuffer xmlOutput;
        private int itemCount;

        private Pattern markPattern;

        XMLOutputBuffer(String markStr) {
            this.markPattern = Pattern.compile(markStr);
            xmlOutput = new StringBuffer();
            itemCount = 0;
        }

        public void append(String str) {
            if (str == null || "".equals(str))
                return;
            this.xmlOutput.append(str);
            Matcher m = this.markPattern.matcher(str);
            while (m.find())
                this.itemCount++;
        }

        public void clearBuffer() {
            xmlOutput = new StringBuffer();
            this.itemCount = 0;
        }

        public StringBuffer getXmlOutput() {
            return xmlOutput;
        }

        public int getItemCount() {
            return itemCount;
        }
    }


}



   

代码中popDividedDataAfterParser() 输出的StringBuffer 可用来初始化一个 StringReader 再给dom4j 的saxReader去解析,这样联合一起用, 想处理多少,就先分出来解析多少,特别适合多线程的生产者和消费者的那种情况,希望对大家有用



分享到:
评论
14 楼 moshalanye 2009-03-18  
stax 提供的事件接口与 SAX 的event-based API 解决的问题是一样的,都是基于事件的,你说的startElement是sax 的event-based API。

stax 在解析xml上更强大些!

在别人的blog看到几篇介绍stax,有兴趣的可以去看看!写的很细心啊!

这边是Eastsun大哥的文章
http://eastsun.iteye.com/blog/129887 
13 楼 cocococoon 2009-03-16  
引用

StAX提供的定位机制  还没碰过   谢谢您的提示  


StAX技术的思路主要是通过streaming的方式读取XML流,然后你可以读取“流”中的一系列事件,如startElement, endElement等等,在每个事件中你可以读取一系列属性。针对这一特点,如果能够根据具体的XML文档结构,在你需要解析的节点上创建解析器,最大的好处就是高效解析巨大的XML文档:一边读取XML,遇到特定的节点的解析器才解析。
12 楼 ispring 2009-03-16  
Digester也是基于事件模型的,应该可以解决你这个问题,不过我没有试过。
Digester对SAX的事件模型进行了扩展,采用Stack来存储当前元素信息,调用方法非常简单,楼主可以试试看。
11 楼 nikos 2009-03-16  
sax解析的话,会出现数据不完整的问题,因为它每次读取的时候是2048的长度,这样可能一个完整句子,就被截断了,严重影响数据的质量

dom4j有一个类似sax的方法,也是基于事件触发机制的,每次只读出一段,然后在内存中形成一个树,一段一段的读,性能上来看是挺好的,但是我试的时候,速度会稍微慢点,这是dom4j官方网站上faq上推荐的(http://www.dom4j.org/dom4j-1.6.1/faq.html#large-doc)

dom4j还是很强大的,它提供了很多的实现方式,有兴趣可以一个个试一下

提示:很多人对dom4j有误解,以为用了dom4j,就跟dom不一样了,其实如果是用一般资料上介绍的方法,它跟dom是类似的,读取文件的时候,在内存中形成整棵树,数据量大了,就oom了
10 楼 xiaojing1005 2009-03-15  
现在我也碰到了这个问题.
在贵帖中挖掘一些思路.
9 楼 moshalanye 2008-07-11  
引用

你可以自己弄一个计数器统计,不在这个计数范围内的就不要处理了,它解析速度是很快的,顶多就浪费一点点cpu资源

如果处理起来麻烦的话,就用StAX提供的定位机制,不过偶没有实际用过,你可以试试看


谢谢您回答,
的确那个第二个需求,的确是我主观思想在作怪了,认为既然经过所有的节点,就必然会把所有节点的数据读取处理并存在内存中,的确也是可以要多少处理多少,就看自己怎么利用现有的条件进行控制了

StAX提供的定位机制  还没碰过   谢谢您的提示  


您说的那个计数器,我当时也有这样想,但是没去实践,也就是您说到的那点,以为每个点都看是否处理,怕浪费cpu,有空还是要实践下,这样才有更多的了解  
在此谢谢各位的回复  
这个是我发的第一个贴子,也是让人很开心的一个开始
8 楼 spiritfrog 2008-07-11  
kusix 写道
同样碰到过LZ的问题,70M的XML,一读服务器就OOM了

研究过sax,感觉很不好用,需要把已有的DOM结构的程序全部废掉重写。

目前采用的是用NIO内存映射方式读文件,然后把XML一段段摘取出来,分段解析处理,

速度还是很不错的,服务器也不OOM了。


如何一段段摘取出来的?
7 楼 quaff 2008-07-10  
看看xquery是不是基于流操作
6 楼 kusix 2008-07-10  
同样碰到过LZ的问题,70M的XML,一读服务器就OOM了

研究过sax,感觉很不好用,需要把已有的DOM结构的程序全部废掉重写。

目前采用的是用NIO内存映射方式读文件,然后把XML一段段摘取出来,分段解析处理,

速度还是很不错的,服务器也不OOM了。
5 楼 Readonly 2008-07-10  
sax的event处理是基于流操作的,不会在内存中构建整个文档树,可以满足你的第2点,但是它只相当于一个简单的iterator,并没有定位机制,你的第一点做不到,不过你可以自己弄一个计数器统计,不在这个计数范围内的就不要处理了,它解析速度是很快的,顶多就浪费一点点cpu资源

如果处理起来麻烦的话,就用StAX提供的定位机制,不过偶没有实际用过,你可以试试看
4 楼 moshalanye 2008-07-10  
   我做的这个出现的需求可以是:
1。 我不需要把XML所有的数据解析完成后,再对数据做处理。  比如xml中有一万条数据(一条数据是一个子节点),我第一次只处理解析1-100的数据,并对数据做业务处理 , 下次再处理101-200的数据,再做业务处理    这样我就不用等全解析完后,再对所有解析后的数据做业务处理,也就是我要几条,就可以解析几条


2。一次性把一个xml 解析后,(假设xml 有几万条数据,那么在内存中就要有这么多的空间来存放)这样挺耗空间的,也不好做成buffer缓存的效果
3 楼 Readonly 2008-07-10  
是的,SAX的event-based API就是你给出的那个接口,具体用法它的文档上就有

你说的不灵活,偶不明白你的具体需求,也就不好给建议了...
2 楼 moshalanye 2008-07-10  
引用

SAX有event-based API,没有必要自己造轮子


我接触xml的时间不是太长,我也希望能获得更多信息,我看了下您的建议,不知道这个event-based 是否是指的org.xml.sax.ContentHandler 接口中的那些  

public void startDocument ()throws SAXException;

public void startElement (
                    String namespaceURI, String localName,
	           String qName, 
                    Attributes atts)
	               throws SAXException;

。。。。。。。。。。


   这些方法,如果是的话,那么这些handle中的方法,实现后在saxParser解析的时候是一直批量的解析下去,总不能解析到了一定数量的 Element 再停下来吧? 我当时也是觉得这样不灵活,用信号量使用wait()来停,我又觉得不是太优雅,所以就没用这个了
    应该还是我知道的东西太少,希望楼上的大虾再指导下,最好有个简单的例子就更加万分感谢了!
1 楼 Readonly 2008-07-10  
SAX有event-based API,没有必要自己造轮子

相关推荐

    kettle批量解析多个xml文件

    标题中的“kettle批量解析多个xml文件”指的是使用Kettle(也称为Pentaho Data Integration,简称PDI)工具处理XML文件的批量操作。Kettle是一款开源的数据集成工具,它提供了丰富的转换和作业来实现数据抽取、转换...

    XML解析工具- TinyXML2 -源代码 - C++

    TinyXML-2 是一个简单、小型、高效的 C++ XML 解析器,可以被轻松集成到其他程序中。它使用文档对象模型(DOM)的方式解析XML,可解析 XML 文档,并根据该文档构建可读取、修改和保存的文档对象模型。 作为XML解析...

    tinyxml2解析XML文件读取数据

    TinyXML-2(通常简称为tinyxml2)是一个轻量级的C++库,专门设计用来解析和操作XML文档。这个库小巧、简单且易于集成到项目中,尤其适合对性能要求不高的应用。 本项目“tinyxml2解析XML文件读取数据”主要关注如何...

    xml文档解析器--c++源代码

    本文将深入探讨使用C++实现XML文档解析的相关知识点,包括XML的基本结构、解析器的工作原理以及如何利用C++进行XML的读取、查询、删除、添加和保存操作。 XML文档的基础结构: XML文档由一系列元素组成,每个元素都...

    Xml之DOM解析--学生成绩管理系统

    在“Xml之DOM解析--学生成绩管理系统”中,我们主要探讨如何使用DOM解析器来读取、处理和更新XML文件,以实现一个简单的学生成绩管理系统的功能。首先,我们需要理解DOM解析的基本流程: 1. **加载XML文档**:使用...

    xml解析文件的两种方式

    - 内存消耗大:对于大型XML文件,DOM可能会消耗大量内存,因为它需要一次性加载整个文档。 - 性能问题:由于需要构建完整的DOM树,解析速度相对较慢。 **2. SAX解析** SAX(Simple API for XML)是一种事件驱动的...

    XML文件,批量进行替换文本内容

    1、可以批量同时处理多个XML文件,适合需要批量修改XML文件中部分内容的需求 2、可以支持文本替换,就是说,只要匹配其中的内容,就可以实现一键将原本的文本内容或者节点的内容,替换成需要替换的内容 可扩展的功能...

    C# 生成xml文件,编码为utf-8方法

    C# 生成 XML 文件,编码为 UTF-8 方法 C# 生成 XML 文件,编码为 UTF-8 方法是指在 C# 编程语言中,使用特定的方法和类来生成 XML 文件,并将其编码为 UTF-8 编码格式的过程。这种方法常用于数据交换、配置文件、...

    雷赛2DM3-EC XML文件

    1. **配置伺服驱动器**:通过读取和写入XML文件,用户可以批量设置多个伺服驱动器的参数,简化了设备的初始化工作。 2. **运动控制**:XML文件可以包含预定义的运动轨迹或动作序列,使得驱动器按照预定的路径执行...

    C写的解析xml文件源代码

    本文将深入探讨使用C语言编写的XML文件解析源码,并讲解如何实现XML文件的基本操作,如查找、增加和删除节点。 在C语言中处理XML文件通常涉及到以下几个关键步骤: 1. **内存管理**:由于C语言没有内置的高级数据...

    天气预报 .xml文件解析

    本项目名为“天气预报 .xml文件解析”,其核心在于利用XML文件来存储天气预报数据,并通过编程手段进行解析,结合SQLite数据库展示和管理这些信息。 首先,我们要理解XML的基本结构。XML是一种自描述性的标记语言,...

    适合嵌入式系统的开源XML解析器

    通过minixml,嵌入式系统开发者能够在受限的环境下实现XML数据的解析和处理,极大地扩展了嵌入式系统的数据处理能力。使用minixml,不仅可以读取配置文件,还可以与服务器进行XML数据交换,或者解析XML格式的协议...

    Java解析XML工具类--(java源码)

    * 本类是专门解析XML文件的,主要用于为系统读取自己的配置文件时提供最方便的解析操作 * @author HX * */ public class XmlManager { /** * 得到某节点下某个属性的值 * @param element 要获取属性的...

    C# XML解析方式

    根据给定文件中的标题、描述、标签以及部分内容,可以总结并深入探讨以下关于C#中XML解析的关键知识点: ### C#中的XML解析方式 #### 1. XML Text Reader(流式解析) - **简介**:在.NET框架中,`XMLTextReader`...

    xml文件解析示范

    这种方式适用于小到中型的XML文件,因为它一次性加载整个文件,内存消耗较大。 2. SAX解析:SAX是一种事件驱动的解析器,它逐行读取XML文件,遇到元素、属性等时触发相应的事件回调。这种方式适用于处理大型XML文件...

    XML.zip_C语言解析XML_Min-xml_XML解析_c语言XML解析器_mini-xml

    在C语言环境中,处理XML文档通常需要使用XML解析器,其中Mini-xml是一个小巧且独立的解决方案。 Mini-xml是由D. J. Butler开发的一个开源项目,它的设计目标是提供一个轻量级的XML解析和生成工具,特别适合于资源...

    解析XML--老师(解析,修改,删除,导出)

    1. **序列化**:将解析后的DOM树或处理过的XML数据重新序列化成XML字符串或文件。 2. **格式化输出**:在导出时可以控制缩进、换行等,使得XML文件更易读。 ### 实际应用 1. **Web服务**:XML常作为SOAP协议的数据...

    XML四种解析方式------DOM SAX JDOM DOM4J

    这种解析方式的优点是可以方便地通过节点关系进行遍历和修改,但缺点是占用内存大,尤其对于大型XML文件,可能导致性能问题。 2. SAX(Simple API for XML) SAX解析器是一种基于事件驱动的解析方式,它不会一次性...

    Android解析xml(3)---Pull解析

    Pull解析器是一种事件驱动的解析方式,它允许应用程序在解析过程中按需获取XML数据,而不是一次性加载整个文档到内存,这使得处理大型XML文件时更为高效。 ### XML解析方式对比 在Android中,XML解析主要有三种...

    解析XML文件例子

    在IT领域,XML文件解析是处理和读取XML数据的关键步骤。以下是一些关于解析XML文件的知识点: 1. XML结构: - 根元素:XML文档必须有一个根元素,所有其他元素都包含在这个根元素内。 - 元素:XML中的基本单位,...

Global site tag (gtag.js) - Google Analytics