2014-03-07 41 views
9

我有一个巨大的XML文件(15 GB)。我想将XML文件中的“文本”标签转换为单个页面。巨大的XML文件到文本文件

示例XML文件:

<root> 
    <page> 
     <id> 1 </id> 
     <text> 
     .... 1000 to 50000 lines of text 
     </text> 
    </page> 
    ... Like wise 2 Million `page` tags 
</root> 

我最初使用DOM解析器,但它MEMORY(有效),将引发JAVA OUT。现在,我已经使用STAX编写了JAVA代码。它运作良好,但表现非常慢。

这是我写的代码:

XMLEventReader xMLEventReader = XMLInputFactory.newInstance().createXMLEventReader(new FileInputStream(filePath)); 
    while(xMLEventReader.hasNext()){ 
     xmlEvent = xMLEventReader.nextEvent(); 

    switch(xmlEvent.getEventType()){ 
    case XMLStreamConstants.START_ELEMENT: 
    if(element == "text") 
     isText = true; 
    break; 
    case XMLStreamConstants.CHARACTERS: 
     chars = (Characters) xmlEvent; 
     if(! (chars.isWhiteSpace() || chars.isIgnorableWhiteSpace())) 
       if(isText) 
       pageContent += chars.getData() + '\n'; 
     break; 
    case XMLStreamConstants.END_ELEMENT: 
     String elementEnd = (((EndElement) xmlEvent).getName()).getLocalPart(); 
     if(elementEnd == "text") 
     { 
      createFile(id, pageContent); 
      pageContent = ""; 
      isText = false; 
     } 
     break; 
    } 
} 

此代码工作良好(忽略任何小错误)。根据我的理解,XMLStreamConstants.CHARACTERS迭代每个文本标签的行。如果TEXT标签中有10000行,则XMLStreamConstants.CHARACTERS会迭代接下来的10000行。有没有更好的方法来提高性能..?

+0

出于好奇,目前需要多长时间才能加载和解析该文件? –

+0

我已解析2GB文件。花了35分钟.. – user1919035

+4

什么是'pageContent'?它是一个“字符串”吗?如果是这样,一个简单的优化就可以使用'StringBuilder'来代替;它可以追加字符串,而不必像字符串''''='那样创建全新的字符串副本(如果你有一个长度的概念,你也可以用一个初始的保留容量来构造它来减少内存重新分配和副本首先)。 –

回答

1

什么是pageContent?它似乎是一个String。一个简单的优化就可以立即使用StringBuilder代替;它可以附加字符串,而不必像字符串String s +=那样创建全新的字符串副本(如果您对长度有所了解,也可以使用初始保留容量来减少内存重新分配和副本)。

连接String s是一个缓慢的操作,因为字符串在Java中是不可变的;每次你拨打a += b它必须分配一个新的字符串,复制a,然后复制b到它的结尾;使每个串联O(n)wrt。两个琴弦的总长度。追加单个字符也一样。另一方面,StringBuilder与追加时的ArrayList具有相同的性能特征。所以,你必须:

pageContent += chars.getData() + '\n'; 

而是改变pageContentStringBuilder,做:

pageContent.append(chars.getData()).append('\n'); 

此外,如果您有上限,这些字符串之一的长度的猜测,你可以通过它给StringBuilder构造函数分配初始容量并减少内存重新分配和完全复制的机会。

顺便说一下,另一个选项是完全跳过StringBuilder并将数据直接写入输出文件(假设您不是先处理数据)。如果您这样做,并且性能受到I/O限制,则在另一个物理磁盘上选择输出文件可能会有所帮助。

0

您的代码看起来很标准。 但是,您可以尝试将FileInputStream包装到BufferedInputStream中,并让我们知道这是否有帮助? BufferedInputstream可以节省很少的OS本地调用,所以有更好的性能。 你必须利用缓冲区大小来获得最佳性能。根据您的JVM内存分配设置一些大小。

+0

实际上,它为您节省了很多系统调用,而实际上8192的默认缓冲区大小几乎适用于所有场合。 – EJP

+0

谢谢..我已经实现了这个..我正在寻找任何其他过程遵循.. – user1919035

1

尝试使用SAX解析器进行解析,因为DOM会尝试解析整个内容并将其放在内存中。正因为如此,你得到了内存异常。 SAX解析器不会一次解析整个内容。

+2

他交换到STAX解析器,它将执行以及SAX解析器 –

+0

我已经使用STAX .. – user1919035

4

我可以看到一些可能的解决办法的事情,可能会帮助你:

  1. 使用BufferedInputStream,而不是一个简单的FileInputStream以减少磁盘操作的数量
  2. 考虑使用StringBuilder创建您pageContent而不是String连接。
  3. 如果你使用2GB示例进行内存绑定,请增加Java堆(-Xmx选项)。

它可以在这样的情况下,挂钩代码分析器(例如Java VisualVM)挺有意思的,你就能够看到什么方法调用中正在代码内缓慢。然后,您可以适当地重点优化。

0
  1. 使用BufferedInputStream围绕FileInputStream.
  2. 不连接的数据。这是对时间和空间的完全浪费,可能有很多空间。写出来,立即得到它。为此,围绕FileWriter使用BufferedWriter
+0

我' m使用FileWriter周围的BufferedWriter .. – user1919035

2

如果解析XML文件是主要问题,请考虑使用VTD-XML,即扩展版本,因为它支持的文件最大为256GB。

由于它基于非抽取式文档解析,因此具有很高的内存效率,并且使用它来使用XPath查询/提取文本的速度也非常快。您可以从here阅读关于此方法和VTD-XML的更多详细信息。