从大文件中打印指定行号之间的行
我有一个非常大的文本文件,大小超过30GB。由于某些原因,我想读取第1000000行到第2000000行的内容,并与用户输入的字符串进行比较。如果匹配成功,我需要把这一行的内容写入另一个文件。
我知道怎么一行一行地读取文件。
input_file = open('file.txt', 'r')
for line in input_file:
print line
但是如果文件太大,读取的速度会受到影响,对吧?那该怎么优化这个过程呢?
6 个回答
一般来说,你不能直接跳到文件中的某一行,因为文本行的长度是变化的,可能占用从一个字节到无数个字节的空间。
不过,如果你需要频繁地在这些文件中查找内容,可以对它们进行索引,也就是在另外的文件中记录每一千行的起始字节位置。这样,你就可以打开文件,使用 file.seek()
方法直接跳到你感兴趣的部分,然后从那里开始读取。
你所有的行都是一样长的吗?如果是这样的话,你可以直接用seek()
跳到你想要的第一行。否则,你就得一个一个地查看整个文件,因为你无法提前知道每一行的开始位置。
input_file = open('file.txt', 'r')
for index, line in enumerate(input_file):
# Assuming you start counting from zero
if 1000000 <= index <= 2000000:
print line
对于小文件,linecache
模块可能会很有用。
如果你在使用Linux,你有没有想过用Python的os.system
或者commands
模块来直接执行一些命令,比如sed
、awk
、head
或者tail
呢?这样可以更方便地处理文件。
比如,你可以运行这个命令:os.system("tail -n+50000000 test.in | head -n10")
这个命令会从文件test.in
中读取第50,000,000到第50,000,010行的内容。在这个StackOverflow的帖子中讨论了不同的调用命令的方法,如果你对性能要求很高,可能还有比os.system
更高效的方式。
在这个unix.stackexchange的讨论中深入探讨了如何使用命令行选择文本文件中特定范围的内容:
- 生成了一个有100,000,000行的文件,命令是
seq 100000000 > test.in
- 读取第50,000,000到50,000,010行
- 测试的顺序没有特别要求
- 使用bash内置的时间命令报告实际时间
结合使用tail
和head
,或者使用sed
,似乎是最快的解决方案。
4.373 4.418 4.395 tail -n+50000000 test.in | head -n10
5.210 5.179 6.181 sed -n '50000000,50000010p;57890010q' test.in
5.525 5.475 5.488 head -n50000010 test.in | tail -n10
8.497 8.352 8.438 sed -n '50000000,50000010p' test.in
22.826 23.154 23.195 tail -n50000001 test.in | head -n10
25.694 25.908 27.638 ed -s test.in <<<"50000000,50000010p"
31.348 28.140 30.574 awk 'NR<57890000{next}1;NR==57890010{exit}' test.in
51.359 50.919 51.127 awk 'NR >= 57890000 && NR <= 57890010' test.in
你可以使用 linecache。
让我引用一下文档中的内容:"linecache模块可以让你从任何文件中获取任意一行,同时它会在内部进行优化,使用缓存来处理从一个文件中读取多行的常见情况。":
import linecache
for i in xrange(1000000, 2000000)
print linecache.getline('file.txt', i)
你可以使用 itertools.islice
这个工具:
from itertools import islice
with open('file.txt') as fin:
lines = islice(fin, 1000000, 2000000) # or whatever ranges
for line in lines:
# do something
当然,如果你的每一行都是固定长度的,你可以直接用 fin.seek()
来跳到行的开头。否则,上面的方法还是需要读取 n
行,直到 islice
开始输出结果,但这样做只是一个方便的方式来限制范围。