如何读取具有特定字符串的文件的最后几行?

时间:2018-03-22 07:20:15

标签: python python-3.x file iterator

我有一个包含数据行和一些说明文本行的日志文件。我想从文件中读取最后10个数据行。我怎么能在Python中做到这一点?我的意思是,有没有比使用

更快的方法
for line in reversed(open("filename").readlines()):

然后解析文件。我想它打开整个文件,如果日志文件很大,则速度很慢。那么有一种方法可以打开文件的末尾并从中读取数据吗?我需要的只是来自文件,Kes的文件的最后10行。如果没有10行有,Kes,则应返回所有行,Kes的行,其顺序与文件中出现的行相同。

4 个答案:

答案 0 :(得分:2)

你必须越过第一(N - 10)行,但你可以聪明地做到这一点。您消耗时间这一事实并不意味着您也必须消耗内存。在您的代码中,您使用readlines()读取所有行并返回它们的列表。这是fileobject本身是一个类似迭代器的对象,你可以使用一个长度有限的容器并将所有行插入到它中,最后它只保留最后N行。在python中,为此可以使用dequemaxlen设置为10:

from collections import deque

with open("filename") as f:
    last_ten_lines =  deque(f,maxlen=10)

关于你的最后一点,如果你想过滤那些单词,Kes的行,最好的办法是循环遍历文件对象的反面。

from itertools import islice
def get_last_n(file_name, n=10):
""" Returns the last N filtered lines. """
    def loop_over():
        with open(file_name) as f:
            for line in reversed(f):
                if ",Kes" in line: 
                    yield line
    return islice(get_last_ten(), N)

答案 1 :(得分:1)

你可以

  • 全部读取,将所有内容存储在一个列表中,全部反转并前10行包含,Kes
    • 您的方法 - 很多的存储和时间
  • 使用Kasramvd的方法,这个方法比这个方法更优雅 - 利用可迭代和islice
  • 自己阅读每一行并检查其中是否有,Kes,如果是,请将其排队:
from collections import deque

# create demodata
with open ("filename","w") as f:
    for n in range (20):
        for p in range(20):
            f.write("some line {}-{}\n".format(n,p))

        f.write("some line with {} ,Kes \n".format(n))

# read demodata
q = deque(maxlen=10)
with open("filename") as f:
    for line in f:           # read one line at time, not huge file at once
        if ',Kes' in line:   # only store line if Kes in it
            q.append(line)   # append line, size limit will make sure we store 10 at most

# print "remebered" data
print(list(q))

输出:

['some line with 10 ,Kes \n', 'some line with 11 ,Kes \n', 'some line with 12 ,Kes \n', 
 'some line with 13 ,Kes \n', 'some line with 14 ,Kes \n', 'some line with 15 ,Kes \n', 
 'some line with 16 ,Kes \n', 'some line with 17 ,Kes \n', 'some line with 18 ,Kes \n', 
 'some line with 19 ,Kes \n']

你不会同时将整个文件放在RAM中,最多11行(curr line + deque持有10行,它只记住其中带有,Kes的行。

答案 2 :(得分:1)

您提出的代码显然效率不高:

  • 您将整个文件读入内存
  • 你完全颠倒了行列表
  • 然后才搜索包含关键字的行。

我可以想象两种可能的算法:

  1. 按正向顺序扫描文件并存储包含关键字的10行,每个新行替换旧版本。代码可能或多或少:

    to_keep = [None] * 10
    index = 0
    for line in file:
        if line.find(keyword) != -1:
            to_keep[index] = line
            index = (index + 1) % 10
    

    如果文件中只有几行包含关键字,并且从后面读取也需要加载文件的很大一部分,那么应该可以接受

  2. 从末尾读取块中的文件,并在每个块上应用上述算法。如果关键字足够频繁,只需要很少的块就会更有效,但会稍微复杂一点:不可能寻找行但只能查找文件中的字节位置,所以你可以从中间开始一行或甚至在多字节字符的中间(考虑UTF-8),所以你应该保留第一个部分行并稍后将其添加到下一个块。

答案 3 :(得分:-1)

导入操作系统    os.popen(' tail -n 10 filepath')。read()