【问题标题】:Efficient reading of 800 GB XML file in Python 2.7在 Python 2.7 中高效读取 800 GB XML 文件
【发布时间】:2013-01-29 13:40:50
【问题描述】:

我正在 python 2.7 中读取一个 800 GB 的 xml 文件并使用 etree 迭代解析器对其进行解析。

目前,我只使用open('foo.txt') 没有缓冲参数。我有点困惑这是我应该采用的方法,还是应该使用缓冲参数或使用来自 io 的东西,例如 io.BufferedReader 或 io.open 或 io.TextIOBase。

我们将不胜感激。

【问题讨论】:

  • 800GB 文本文件?!上帝之母...你们在什么时候想说将其放入数据库是个好主意?
  • @Makoto,更糟糕的是标题是文本,但正文却是 XML
  • 我只是希望你的文件实际上是有效的 XML...
  • @JBernardo,也许他们必须这样做才能满足营销部门的流行语合规性。
  • 如果你必须知道,它是西班牙语维基百科的完整修订转储。

标签: python xml python-2.7 file io


【解决方案1】:

默认情况下,标准open() function 已经返回一个缓冲文件(如果在您的平台上可用)。对于通常完全缓冲的文件对象。

通常这里的意思是 Python 把它留给 C stdlib 实现;它使用fopen() call(Windows 上的wfopen() 支持UTF-16 文件名),这意味着选择了文件的默认缓冲;在 Linux 上,我相信这将是 8kb。对于像 XML 解析这样的纯读取操作,这种类型的缓冲正是你想要的。

iterparse 完成的 XML 解析以 16384 字节 (16kb) 的块读取文件。

如果要控制缓冲区大小,请使用buffering 关键字参数:

open('foo.xml', buffering=(2<<16) + 8)  # buffer enough for 8 full parser reads

这将覆盖默认缓冲区大小(我希望它匹配文件块大小或其倍数)。根据this article 增加读取缓冲区应该的帮助,并且使用至少 4 倍于预期读取块大小加上 8 个字节的大小将提高读取性能。在上面的示例中,我将其设置为 ElementTree 读取大小的 8 倍。

io.open() function 代表新的 Python 3 I/O 对象结构,其中 I/O 已拆分为新的类类型层次结构,以提供更大的灵活性。代价是更多的间接性,数据必须通过更多的层,Python C 代码自己做更多的工作,而不是把它留给操作系统。

你可以试试看io.open('foo.xml', 'rb', buffering=2&lt;&lt;16)是否会表现得更好。以rb 模式打开会给你一个io.BufferedReader instance。

你确实不想使用io.TextIOWrapper;底层的 expat 解析器需要原始数据,因为它会自行解码您的 XML 文件编码。它只会增加额外的开销;如果你在r(文本模式)中打开,你会得到这种类型。

使用io.open() 可能会为您提供更大的灵活性和更丰富的API,但底层C 文件对象是使用open() 而不是fopen() 打开的,并且所有缓冲都由Python io.BufferedIOBase 实现处理。

我认为您的问题将是处理这个野兽,而不是文件读取。无论如何,在读取 800GB 文件时,磁盘缓存几乎都会被拍摄。

【讨论】:

  • ElementTree 真的有用吗?它不会尝试将整棵树都放入内存中吗?
  • @poke:这就是iterparse() 的为。它使用 ElementTree API 为您提供事件驱动的解析,因此您可以根据需要再次释放元素。
  • 那么你能澄清一下在这种情况下使用 open() 和 io.open() 有什么区别吗? file 和 io.TextIOWrapper 之间的区别(因为这是 io.open 返回的)?您还可以解释一下通常完全缓冲是什么意思吗?既然我读到一个文本文件是行缓冲的,我是否必须为此打开它作为“rb”?
  • @MikeS:我今晚没时间了;那得等到早上。一般这里不需要添加额外的分层,文件上的open()会被完全缓冲; ttys(你的终端)通常是行缓冲的; b 二进制模式在那里没有区别。明天我会弄清楚“一般”是什么意思。
  • @MikeS:在那里,扩展到涵盖缓冲和open() 与io.open() 的所有方面。我会使用第一个。
【解决方案2】:

我还没有尝试过处理这种史诗般的 xml 文件,但上次我不得不处理大型(且相对简单)的 xml 文件,我使用了sax parser。

它基本上为您提供每个“事件”的回调,并留给您存储您需要的数据。您可以提供一个打开的文件,这样您就不必一次全部阅读。

【讨论】:

  • ElementTree 的 iterparse() 建立在 sax 解析器之上。
【解决方案3】:

你尝试过惰性函数吗?:Lazy Method for Reading Big File in Python?

这似乎已经回答了你的问题。但是,我会考虑使用这种方法将您的数据写入数据库,mysql 是免费的:http://dev.mysql.com/downloads/,NoSQL 也是免费的,并且可能更适合涉及写入 800gb 数据或类似数量的操作:http://www.oracle.com/technetwork/database/nosqldb/downloads/default-495311.html

【讨论】:

  • 同意,无需重新发明轮子。我认为他们在帖子中也进一步使用了 .iter()。
猜你喜欢
  • 2018-11-26
  • 2014-07-21
  • 2015-04-24
  • 1970-01-01
  • 2012-07-09
  • 1970-01-01
  • 2016-06-23
  • 2019-08-26
  • 1970-01-01
相关资源
最近更新 更多