【问题标题】:Huge XML file to text files巨大的 XML 文件到文本文件
【发布时间】:2014-04-10 02:43:17
【问题描述】:

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

示例 XML 文件:

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

我最初使用 DOM 解析器,但它会抛出 JAVA OUT OF MEMORY(Valid)。现在,我已经使用 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 行。有没有更好的方法来提高性能..?

【问题讨论】:

  • 只是出于好奇,您目前需要多长时间才能加载和解析该文件?
  • 我已经解析了 2GB 的文件。花了35分钟..
  • 什么是pageContent?是String吗?如果是这样,立即进行的一项简单优化是改用StringBuilder;它可以附加字符串,而不必像Strings += 那样制作字符串的全新副本(如果您知道开始的长度,您也可以使用初始保留容量构建它以减少内存重新分配和副本与)。
  • 恐怕你主要受限于通过 2GB 文件所需的基本时间......我对 StAX 不够熟悉,但在 SAX 中的字符事件不是每行而是每个解析缓冲区中断,这可能是多行或一行的一部分,具体取决于行的长度以及文本内容开始时您碰巧在解析器的输入缓冲区中的位置。
  • 为什么不使用可用的库来提取内容。 WikiXMLJ 是一个非常好的库 code.google.com/p/wikixmlj>

标签: java xml


【解决方案1】:

我可以看到一些可能对您有所帮助的解决方案:

  1. 使用BufferedInputStream 而不是简单的FileInputStream 来减少磁盘操作次数
  2. 考虑使用StringBuilder 来创建您的pageContent 而不是字符串连接。
  3. 增加您的 Java 堆(-Xmx 选项),以防您的 2GB 示例占用了内存。

在这种情况下,连接代码分析器(例如Java VisualVM)可能会非常有趣,因为这样您就可以准确地看到代码中哪些方法调用很慢。然后,您可以适当地集中优化。

【讨论】:

    【解决方案2】:

    pageContent 是什么?它似乎是String。立即进行的一项简单优化是改用StringBuilder;它可以附加字符串,而不必像Strings += 那样制作字符串的全新副本(如果您知道开始的长度,您也可以使用初始保留容量构建它以减少内存重新分配和副本与)。

    连接Strings 是一个缓慢的操作,因为字符串在Java 中是不可变的;每次调用a += b它必须分配一个新字符串,将a复制到其中,然后将b复制到它的末尾;使 each 连接 O(n) wrt。两个字符串的总长度。附加单个字符也是如此。另一方面,StringBuilder 在附加时具有与ArrayList 相同的性能特征。那么你在哪里:

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

    改为将 pageContent 更改为 StringBuilder 并执行以下操作:

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

    此外,如果您猜测其中一个字符串的长度上限,您可以将其传递给StringBuilder 构造函数以分配初始容量并减少内存重新分配和完整复制的机会必须完成。

    顺便说一句,另一种选择是完全跳过StringBuilder 并将数据直接写入输出文件(假设您没有先以某种方式处理数据)。如果您这样做,并且性能受 I/O 限制,那么选择不同物理磁盘上的输出文件会有所帮助。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:
        1. FileInputStream. 周围使用BufferedInputStream
        2. 不要连接数据。这完全是浪费时间和空间,可能会浪费很多空间。得到它立即写出来。为此,请在 FileWriter 周围使用 BufferedWriter

        【讨论】:

          【解决方案5】:

          尝试使用 SAX 解析器进行解析,因为 DOM 会尝试解析整个内容并将其放入内存中。因此,您会遇到内存异常。 SAX 解析器不会一次性解析全部内容。

          【讨论】:

          • 他换成了 STAX 解析器,它的性能与 SAX 解析器一样好
          【解决方案6】:

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

          【讨论】:

          • 实际上它为您节省了 很多 个系统调用,而且实际上默认的缓冲区大小 8192 几乎可以满足所有场合。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-03-31
          • 2018-07-31
          • 2014-07-22
          • 2010-09-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多