代码之家  ›  专栏  ›  技术社区  ›  Jaakko Seppälä

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

  •  1
  • Jaakko Seppälä  · 技术社区  · 8 年前

    我有一个日志文件,其中有数据行和一些解释文本行。我想从文件中读取最后10行数据。如何在Python中执行此操作?我是说,有没有比使用更快的方法

    for line in reversed(open("filename").readlines()):
    

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

    4 回复  |  直到 8 年前
        1
  •  2
  •   Mazdak    8 年前

    你必须越过第一条(N-10)线,但你可以用一种聪明的方式来完成。你在浪费时间并不意味着你也必须消耗内存。在您使用的代码中 readlines() 读取所有行并返回它们的列表。这是当 fileobject 它本身是一个类似迭代器的对象,您可以使用长度受限的容器并将所有行插入其中,最后只保留最后的N行。在python中,可以使用 deque 及其 maxlen 为此,请设置为10:

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

    关于最后一点,如果要过滤包含单词的行 ,Kes 最好的方法是在file对象的背面循环。

    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)
    
        2
  •  1
  •   Patrick Artner    8 年前

    你可以

    • 全部读取、全部存储在列表中、全部反转并获取包含以下内容的前10行 ,Kes
      • 你的方法-需要 地块 存储和时间
    • 使用Kasramvd的方法,francly比这一个优雅得多-利用iterable和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 在里面。

        3
  •  1
  •   Serge Ballesta    8 年前

    您提出的代码显然没有效率:

    • 您将整个文件读入内存
    • 完全反转行列表
    • 只有这样,您才能搜索包含关键字的行。

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

    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),因此应该保留第一个部分行,稍后将其添加到下一个块。

        4
  •  -1
  •   Abhijith VA    8 年前

    导入操作系统 操作系统。popen('tail-n 10 filepath')。读取()