如何在Python中高效地迭代两个文件?

4

我有两个文本文件,这两个文件应该有很多匹配的行,我想要精确地找出这两个文件之间有多少行是相同的。问题在于,这两个文件都相当大(一个文件约为3GB,另一个文件超过16GB)。所以,使用read()或readlines()将它们读入系统内存可能会非常棘手。有什么提示吗?我正在编写的代码基本上只使用了2个循环和一个if语句来进行比较。


1
这些文件已经排序了吗?如果没有,你能否预先对它们进行排序? - johnsyweb
@TheFoxx 这些需要匹配的行是否会按照相同的顺序出现,中间会有更多/更少的行,还是顺序被打乱了? - Noel Evans
被排序的文件并不特别重要,即使已经排序,小文件中相邻的两行可能会被大文件中数百万行的文本分开。 - TheFoxx
@TheFoxx:人们询问排序的原因是,如果这两个文件已经排序,你只需要在任何给定时间内在内存中保留每个文件的一行。潜在的“数百万行”分隔连续匹配是无关紧要的。 - John Y
@JohnY 但是我如何在任何给定时间仅将单行存储在内存中?下面有人提到使用readline(),但那个回答被踩了? - TheFoxx
显示剩余5条评论
3个回答

2

由于输入文件非常大,如果您关心性能,应该考虑简单地使用grep -f-f选项从文件中读取模式,因此根据您所需的确切语义,它可能会做到您需要的。 您可能还想使用-x选项,仅匹配整行。 因此,在Python中,整个过程可能看起来像这样:

child = subprocess.Popen(['grep', '-xf', file1, file2], stdout=subprocess.PIPE)
for line in child.stdout:
    print line

这看起来非常有趣,你能解释一下child变量在做什么吗?就像subprocess.Popen那一部分? - TheFoxx
我只是在Unix中搜索文件,然后又遇到了内存问题,不得不在它崩溃我的机器之前终止该进程。 - TheFoxx

1

为什么不使用Unix的grep?如果您希望您的解决方案具有平台独立性,则此解决方案将无法使用。但在Unix中,它可以工作。从您的Python脚本中运行此命令。

grep --fixed-strings --file=file_B file_A > result_file

此问题似乎是采用map-reduce的一个很好理由。
更新0:为了阐明。--fixed-strings =将PATTERN解释为固定字符串列表,由换行符分隔,其中任何一个都要匹配。和--file=从FILE获取模式,每行一个。
所以我们正在从file_B中获取模式,并将其与file_A中的内容匹配,fixed-strings将它们视为文件中的模式序列。希望这样更清楚。
由于您想要匹配行的计数,对上述grep进行轻微修改,我们得到了计数。
grep --fixed-strings --file=file_B file_A | wc -l
更新1:你可以这样做。首先逐个文件逐行阅读,不要将整个文件读入内存。当你读取一行时,计算此行的MD5哈希并将其写入另一个文件中。当你对两个文件都这样做时,你会得到两个填充有MD5哈希的新文件。我希望这两个文件在大小上大大减小,因为MD5是16字节,无论输入字符串如何。现在,你可能可以使用grep或其他差异技术进行操作,几乎没有内存问题。 – Srikar 3分钟前编辑 更新2(几天后):你能做到这点吗?在mysql中创建2个表table1, table2。两个表只有两个字段id、data。将两个文件逐行插入这两个表中。之后运行一个查询以查找重复项的数量。必须处理两个文件。这是事实。我们不能逃避。现在可以优化如何找到重复项。MySQL就是其中之一。它删除了许多需要进行的操作,例如RAM空间、索引创建等。

我曾考虑使用Unix的grep命令,但是我更习惯使用Python(不过我确实需要学习更多关于Unix的知识!)。我不太理解你那段代码,显然file_B和file_A是我的文件,但是我不明白"fixed strings bit"的意思。 - TheFoxx
好的,这很简单。既然你说这两个文件中的大部分内容都是相同的,那么这个单一命令就可以为你找到它。请查看我上面的更新。 - Srikar Appalaraju
谢谢。问题再次出在内存上,运行这个grep命令几乎让我的计算机崩溃了。我不得不在它真正崩溃之前终止它。 - TheFoxx
1
你能做到吗?首先,逐行单独处理每个文件,不要将整个文件读入内存。当你读取一行时,计算该行的MD5哈希值并将其写入另一个文件中。当你对这两个文件进行操作后,你会得到两个新文件,其中填充了MD5哈希值。我希望这两个文件的大小比原始文件要小得多,因为MD5是16字节,无论输入字符串如何。现在,你可以使用grep或其他差异技术来解决很小或没有内存问题。 - Srikar Appalaraju
还需要一些。这是一次性的工作吗?这项工作需要每天运行吗?有什么限制条件?文件大小每次都会这么大吗? - Srikar Appalaraju
显示剩余3条评论

0

非常感谢大家的建议!但是我最终做的事情非常简单。我尝试了像这样读取整个文件的方法。

file = open(xxx,"r")
for line in file:
      if.....

我最终做的是:
for line in open(xxx)
    if.....

第二种方法逐行读取文件。这非常耗时,但我已经基本上接受了,没有什么神奇的方式可以在很短的时间内完成这个任务 :(

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接