2

我的目标是解析 25 GB 的 XML 数据。下面给出了此类数据的一个示例:

<Document>
<Data Id='12' category='1'  Body="abc"/>
<Data Id='13' category='1'  Body="zwq"/>
.
.
<Data Id='82018030' category='2' CorrespondingCategory1Id='13' Body="pqr"/>

但是..考虑到我拥有的“25 GB”数据...我的方法效率很低。请提出一些改进我的代码的方法或替代方法。还请包含一个小示例代码,以使事情更清楚。

4

6 回答 6

4

您可能会发现 SAX 解析器更适合此任务。SAX 解析器不是构建 DOM,而是将 XML 文件转换为元素流并调用您提供的函数来处理每个元素。

好消息是,与 DOM 解析器相比,SAX 解析器可以非常快速且节省内存,有些甚至不需要一次提供所有 XML,当您拥有 25 GB 的 XML 时,这将是理想的选择。

不幸的是,如果您需要任何上下文信息,例如“我想要标签<B>,但只有在标签内<A>”,您必须自己维护它,因为解析器给您的只是“开始标签<A>,开始标签<B>,结束标签<B>,结束标签<A>”。它从不明确告诉你 tag<B>在 tag 内<A>,你必须从你所看到的中弄清楚。一旦你看到一个元素,它就消失了,除非你自己记住它。

这对于复杂的解析工作来说非常麻烦,但你的工作可能是可以管理的。

碰巧 Python 的标准库在xml.sax. 您可能想要类似xml.sax.xmlreader.IncrementalParser.

于 2012-04-05T19:48:16.427 回答
0

查看您的问题后,我的第一个建议是使用关系数据库,例如 MySQL 或 sqlite。将您的 XML 数据放入此表单并不难,然后查询该数据将更加直接和快速。

于 2012-04-05T19:41:20.003 回答
0

您的初始算法在 O(n^2) 中运行,这对于 25GB 的数据将非常慢。理想情况下,您会将其降低到 O(n) 或 O(n log n)。在没有关于数据的任何其他信息(例如类别 1 或 2 是否更小等)的情况下,您可以执行以下操作(即 O(n)):

from lxml import objectify
f=open('myfile25GB', 'r')
text=f.read()
root=objectify.fromstring(text)

cat_one_bodies = {}
for e in root.attrib['Document'].row:
    category = int(e.attrib['category'])
    body = e.attrib['Body']
    if category == 1:
        e_id = int(e.attrib['Id'])
        cat_one_bodies[e_id] = body
    else: #Assuming there are only 2 categories
        cat_one_id = int(e.attrib['CorrespondingCategory1Id'])
        print "Cat1 Body: '%s' Cat2 Body: '%s'" % (body, cat_one_bodies[cat_one_id])

虽然这不会解析您的文件,但希望它向您展示了这个想法。它可能会使用相当多的内存(因为它维护字典中的所有 category1 主体),所以这可能是一个考虑因素。

于 2012-04-05T20:04:11.783 回答
0

在目前在 Saxon-EE 中实现的 XSLT 3.0(草案)中,您可以编写一个流式转换来解决这个问题,如下所示:

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:map="http://www.w3.org/2005/xpath-functions/map">
<xsl:mode streamable="yes"/>
<xsl:template match="/">
  <xsl:iterate select="Document/Data">
    <xsl:param name="map" select="map{}"/>
    <xsl:choose>
      <xsl:when test="@category='1'">
        <xsl:next-iteration>
          <xsl:with-param name="map" select="map:put($map, string(@Id), string(@Body))"/>
        </xsl:next-iteration>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="'Cat1 Body: ', 
                              $map(@CorrespondingCategoryId), 'Cat2 Body', @Body"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:iterate>
</xsl:template>

我还没有对此进行测试(这是四天假期前夕的深夜......)但如果您有兴趣采用这种方法,我将很乐意提供帮助。XSLT 3.0 仍然是一个草案规范并且相当流畅。它的重点是使用流式方法解决此类问题,该方法使用有界内存处理非常大的文档。Saxon-EE 9.4 实现了规范的快照。

于 2012-04-05T22:19:44.833 回答
0

如果 ID 是按升序排列的,那么您可以推出自己的函数来读取文件中任何位置的元素。然后你可以扫描整个文件,对于每个元素,你可以使用二进制搜索算法找到对应的元素。该事物将在 O(n log n) 中运行,同时仍使用可忽略不计的内存量。

于 2012-04-05T22:32:18.043 回答
0

尝试使用 lxml 中的 iterparse。我认为它将适合您希望处理的问题。

于 2012-05-08T11:53:24.447 回答