21

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

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

一个正确方向的观点将不胜感激。

4

3 回答 3

17

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

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

XML 解析由iterparse16384 字节 (16kb) 的块读取文件。

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

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

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

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

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

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

Usingio.open()可能会给你更多的灵活性和更丰富的 API,但是底层的 C 文件对象是使用open()而不是打开的fopen(),并且所有的缓冲都由 Pythonio.BufferedIOBase实现处理。

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

于 2013-02-13T21:18:08.390 回答
1

你尝试过惰性函数吗?:在 Python 中读取大文件的惰性方法?

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

于 2013-02-13T21:20:17.537 回答
1

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

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

于 2013-02-13T23:10:28.933 回答