获取文件中出现字符串的倒数第四行

时间:2018-06-19 18:34:06

标签: python

我当前正在搜索包含IP地址的日志文件。
日志示例:

10.1.177.198 Tue Jun 19 09:25:16 CDT 2018
10.1.160.198 Tue Jun 19 09:25:38 CDT 2018
10.1.177.198 Tue Jun 19 09:25:36 CDT 2018
10.1.160.198 Tue Jun 19 09:25:40 CDT 2018
10.1.177.198 Tue Jun 19 09:26:38 CDT 2018
10.1.177.198 Tue Jun 19 09:27:16 CDT 2018
10.1.177.198 Tue Jun 19 09:28:38 CDT 2018

我目前可以从日志的最后一行获取IP地址。我还可以搜索具有相同IP地址的所有行号。

如果日志中的最后一个IP地址在日志中列出了3次或多次,我如何获得该IP地址倒数第三次出现的行号?

例如,我要获取此行的行号:

10.1.177.198 Tue Jun 19 09:26:38 CDT 2018

或者更好的是,只需打印整行。

这是我的代码示例:

import re

def run():

    try:
        logfile = open('read.log', 'r')

        for line in logfile:  
            x1 = line.split()[0]
            for num, line in enumerate(logfile, 0):
                if x1 in line:
                    print("Found " + x1 + " at line:", num)

        print ('Last Line: ' + x1)

        logfile.close
    except OSError as e:
        print (e)

run()

我列出了特定IP地址所在的所有行号。

print("Found " + x1 + " at line:", num)

我要打印其中“ num”是行号列表中倒数第三的行。

我的总体目标是从日志文件的最后一行中获取IP地址。然后检查它以前是否已被列出3次以上。如果有的话,我想找到地址的倒数第3位并获取行号(或仅打印该行上列出的地址和日期)

3 个答案:

答案 0 :(得分:1)

跟踪所有事件,并从最后一个开始打印第三个事件。可以使用heapq进行优化。

def run():
    try:
        logfile = open('log.txt', 'r')

        ip_address_line_number = dict()
        for index,line in enumerate(logfile,1):  
            x1 = line.split()[0]
            log_time = line.split()[4]
            if x1 in ip_address_line_number : 
                ip_address_line_number[x1].append((index,log_time))
            else:
                ip_address_line_number[x1] = [(index,log_time)]

        if x1 in ip_address_line_number and len(ip_address_line_number.get(x1,None)) > 2:
            print('Last Line: '+ ip_address_line_number[x1][-3].__str__())
        else:
            print(x1 + ' has 0-2 occurences')
        logfile.close
    except OSError as e:
        print (e)

run()

答案 1 :(得分:0)

另一种查看方式,如果文件是反向读取的

  • 第一个ip的第三个观察的行数据是什么?
  • 在文件中,必须至少有3+1个观察到的第一个IP。

有些many tools可以提供更简单的代码,但这是一种灵活的通用方法,旨在提高内存效率。大致来说,让我们:

  1. 向后读取文件
  2. 最多统计3+1个观察
  3. 返回上一个观察结果

给出

文件test.log

# test.log 
10.1.177.198 Tue Jun 19 09:25:16 CDT 2018
10.1.160.198 Tue Jun 19 09:25:38 CDT 2018
10.1.177.198 Tue Jun 19 09:25:36 CDT 2018
10.1.160.198 Tue Jun 19 09:25:40 CDT 2018
10.1.177.198 Tue Jun 19 09:26:38 CDT 2018
10.1.177.198 Tue Jun 19 09:27:16 CDT 2018
10.1.177.198 Tue Jun 19 09:28:38 CDT 2018

reverse_readline() generator的代码,我们可以编写以下代码:

代码

def run(filename, target=3, min_=3):
    """Return the line number and data of the `target`-last observation.

    Parameters
    ----------
    filename : str or Path
        Filepath or name to file.
    target : int
        Number of final expected observations from the bottom, 
        e.g. "third to last observation." 
    min_ : int
        Total observations must exceed this number.

    """
    idx, prior, data = 0, "", []    
    for i, line  in enumerate(reverse_readline(filename)):
        ip, text = line.strip().split(maxsplit=1)
        if i == 0:
            target_ip = ip
        if target == 0:
            idx, *data = prior
        if ip == target_ip:
            target -= 1                                      
            prior = i, ip, text

    # Edge case
    total_obs = prior[0]
    if total_obs < min_:
        print(f"Minimum observations was not met.  Got {total_obs} observations.")
        return None

    # Compute line number
    line_num = (i - idx) + 1                               # add 1 (zero-indexed)
    return  [line_num] + data

演示

run("test.log")
# [5, '10.1.177.198', 'Tue Jun 19 09:26:38 CDT 2018']

第二次观察:

run("test.log", 2)
# [6, '10.1.177.198', 'Tue Jun 19 09:27:16 CDT 2018']

所需的最少观察值:

run("test.log", 2, 7)
# Minimum observations was not met.  Got 6 observations.

根据需要添加错误处理。


详细信息

注意:“观察”是包含目标ip的行。

  • 我们迭代高效的reverse_readline()生成器。
  • target_ip是从反转文件的“第一”行确定的。
  • 我们仅对第三次观察感兴趣,因此我们无需保存所有信息。因此,当我们进行迭代时,我们一次只能将一个观测值暂时保存到prior(减少内存消耗)。
  • target是一个计数器,每次观察后都会递减。当target计数器达到0时,将保存prior观测值,直到发生器耗尽为止。
  • prior是一个元组,其中包含用于最后观察目标ip地址(即索引,地址和文本)的行数据。
  • 生成器用尽了精力来确定文件的total_obs长度和文件长度,该文件用于计算line_num ber。
  • 返回计算出的行号和行数据。

答案 2 :(得分:0)

使用pandas会很短:

import pandas as pd
df = pd.read_fwf('read.log', colspecs=[(None, 12), (13, None)], header=None, names=['IP', 'time'])

lastIP = df.IP[df.index[-1]]
lastIP_idx = df.groupby('IP').groups[lastIP]

n = 3
if len(lastIP_idx) >= n:
    print('\t'.join(list( df.loc[lastIP_idx[-n]] )))
else:
    print('occurence number of ' + lastIP + ' < ' + str(n))