从大文件中打印指定行号之间的行

4 投票
6 回答
3438 浏览
提问于 2025-04-18 16:12

我有一个非常大的文本文件,大小超过30GB。由于某些原因,我想读取第1000000行到第2000000行的内容,并与用户输入的字符串进行比较。如果匹配成功,我需要把这一行的内容写入另一个文件。

我知道怎么一行一行地读取文件。

input_file = open('file.txt', 'r')
for line in input_file:
    print line

但是如果文件太大,读取的速度会受到影响,对吧?那该怎么优化这个过程呢?

6 个回答

0

一般来说,你不能直接跳到文件中的某一行,因为文本行的长度是变化的,可能占用从一个字节到无数个字节的空间。

不过,如果你需要频繁地在这些文件中查找内容,可以对它们进行索引,也就是在另外的文件中记录每一千行的起始字节位置。这样,你就可以打开文件,使用 file.seek() 方法直接跳到你感兴趣的部分,然后从那里开始读取。

1

你所有的行都是一样长的吗?如果是这样的话,你可以直接用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模块可能会很有用。

1

如果你在使用Linux,你有没有想过用Python的os.system或者commands模块来直接执行一些命令,比如sedawkhead或者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内置的时间命令报告实际时间

结合使用tailhead,或者使用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
2

你可以使用 linecache

让我引用一下文档中的内容:"linecache模块可以让你从任何文件中获取任意一行,同时它会在内部进行优化,使用缓存来处理从一个文件中读取多行的常见情况。"

import linecache

for i in xrange(1000000, 2000000)
    print linecache.getline('file.txt', i)
10

你可以使用 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 开始输出结果,但这样做只是一个方便的方式来限制范围。

撰写回答