上一篇文章《python3实现归并排序算法图文详解》中,我们了解了归并排序算法的基本使用逻辑。这一篇文章我们对这个逻辑进行一个延伸,从内存延伸到对硬盘上的文件进行排序,也就是所谓的外部排序。
操作场景
所谓的大文件就是无法一次性全部读到内存中的文件。
为了操作不真的把我机器的内存都榨干,这里假设机器的内存是300MB,刨除一些系统占用,规定每次读到内存的文件大小不能超过200MB。
我又用下面的程序创建了一个1GB大小的文件
with open('bigfile.txt','a') as f:
for i in range(100000000):
f.write(str(random.randint(100000000,999999999))+'\r\n')
这个文件一共有1亿行,每一行以字符串格式存储着一位9位数,并在末尾加上了换行符,所以每行占据10B,算下来一共有10亿B也就是1GB。
下面要实现的就是在内存占用每次不超过200MB的情况下将这个1GB的文件进行排序。硬盘空间很大,随意使用。
思路分析
大家应该还记得《python3实现归并排序算法图文详解》中的两张图
针对一个长列表,我们先分割为两个小列表,并将小列表排好序。利用两个游标从左到右移动,我们对两个已经各自排好序的列表合并成了一个新的有序列表。
这里的小技巧就是利用了递归的思路,对每一个小的列表都进行了归并排序。
但是我们这个问题不需要这么麻烦。首先将1GB的大文件分解为5个200MB的小文件,这里的小文件就是可以直接读进内存一次性排序。
然后直接对这5个小文件读进内存进行排序。
最后再利用归并的思路,通过5个游标分别在5个文件中逐行从左往右移动,将每次最小的一行读到新文件中,达到排序的效果,如下图
代码实现
文件分割
首先来对文件进行分割,每次从原文件读200MB的整行数,然后保存到一个新文件。
这里要补充下python中读取文件的几个方法。
f.readlines(n)
可以接一个参数n
,注意这里的n
并不是行数,而是以字节为单位的大小,表示当读到某行时总大小超过了n就不再读下一行。同理,f.readline(n)
也可以传递n,表示读取下一行的前n个字节,如果下一行总大小少过n就读取整行。而在某些没有换行符的文件中就不能用这两种读取的方法了