优化在Python中查找和替换大文件

时间:2010-09-26 22:26:57

标签: python optimization replace

我是Python的完全初学者或任何严肃的编程语言。我终于得到了一个原型代码,但我觉得它太慢了。

我的目标是根据我拥有的csv文件,在整数目录中找到并替换所有文件(它们是csv)中的一些中文字符。这些文件按年份编号很好,例如2000-01.csv,并且是该目录中唯一的文件。

我将遍历大约25个文件,每个文件在500mb附近(大约一百万行)。我将使用的字典将有大约300个元素,我将unicode(中文字符)更改为整数。我尝试了一个测试运行,假设一切都线性增长(?),看起来它需要大约一个星期才能运行。

提前致谢。这是我的代码(不要笑!):

# -*- coding: utf-8 -*-

import os, codecs

dir = "C:/Users/Roy/Desktop/test/"

Dict = {'hello' : 'good', 'world' : 'bad'}

for dirs, subdirs, files in os.walk(dir):
    for file in files:
        inFile = codecs.open(dir + file, "r", "utf-8")
        inFileStr = inFile.read()
        inFile.close()
        inFile = codecs.open(dir + file, "w", "utf-8")
        for key in Dict:
            inFileStr = inFileStr.replace(key, Dict[key])
        inFile.write(inFileStr)
        inFile.close()

4 个答案:

答案 0 :(得分:13)

在您当前的代码中,您将立即将整个文件读入内存。由于它们是500Mb文件,这意味着500Mb字符串。然后你反复替换它们,这意味着Python必须用第一个替换创建一个新的500Mb字符串,然后销毁第一个字符串,然后为第二个替换创建第二个500Mb字符串,然后销毁第二个字符串,等等,每次更换。事实证明,来回复制数据非常多,更不用说使用大量内存了。

如果您知道替换将始终包含在一行中,您可以通过迭代来逐行读取该文件。 Python将缓冲读取,这意味着它将得到相当优化。您应该以新名称打开一个新文件,以便同时写入新文件。依次在每一行上执行替换,并立即写出。执行此操作将大大减少使用的内存量在进行替换时来回复制的内存量:

for file in files:
    fname = os.path.join(dir, file)
    inFile = codecs.open(fname, "r", "utf-8")
    outFile = codecs.open(fname + ".new", "w", "utf-8")
    for line in inFile:
        newline = do_replacements_on(line)
        outFile.write(newline)
    inFile.close()
    outFile.close()
    os.rename(fname + ".new", fname)

如果你不能确定他们是否总是在一条线上,事情会变得更加困难;您必须使用inFile.read(blocksize)手动读取块,并仔细跟踪块末尾是否存在部分匹配。这并不容易,但通常仍然值得避免使用500Mb字符串。

另一个重大改进是如果你可以一次性完成替换,而不是按顺序尝试一大堆替换。有几种方法可以做到这一点,但最适合的方式完全取决于你要替换的东西和什么。为了将单个字符转换为其他字符,unicode对象的translate方法可能很方便。你传递一个dict映射unicode代码点(作为整数)到unicode字符串:

>>> u"\xff and \ubd23".translate({0xff: u"255", 0xbd23: u"something else"})
u'255 and something else'

要替换子字符串(而不仅仅是单个字符),可以使用re模块。 re.sub函数(以及编译的正则表达式的sub方法)可以将可调用的(函数)作为第一个参数,然后为每个匹配调用该函数:

>>> import re
>>> d = {u'spam': u'spam, ham, spam and eggs', u'eggs': u'saussages'}
>>> p = re.compile("|".join(re.escape(k) for k in d))
>>> def repl(m):
...     return d[m.group(0)]
...
>>> p.sub(repl, u"spam, vikings, eggs and vikings")
u'spam, ham, spam and eggs, vikings, saussages and vikings'

答案 1 :(得分:2)

我认为您可以通过一次读取一行并将其写入(在已经建议的正则表达式替换之后)到临时文件中来大大降低内存使用(从而限制交换使用并使事情更快),然后将文件移动到替换原来的。

答案 2 :(得分:1)

一些事情(与优化问题无关):

dir + file应为os.path.join(dir, file)

您可能希望不重用infile,而是打开(并写入)单独的outfile。这也不会提高性能,但这是一种很好的做法。

我不知道你是I / O绑定还是cpu绑定,但是如果你的cpu利用率非常高,你可能想要使用线程,每个线程在不同的文件上运行(所以使用四核处理器,你将同时读/写4个不同的文件。

答案 3 :(得分:0)

打开文件读/写('r +')并避免双重打开/关闭(以及可能关联的缓冲区刷新)。此外,如果可能,请不要回写整个文件,在替换文件内容后仅查找和回写更改的区域。读取,替换,写入更改的区域(如果有)。

这仍然不会对性能太多有很多帮助:我会分析并确定性能命中的实际位置,然后再进行优化。它可能只是从磁盘读取数据的速度非常慢,而且你在Python中无法做到这一点。