返回日志文件中所有匹配的行

0 投票
3 回答
815 浏览
提问于 2025-04-17 19:40

我需要一些关于循环的帮助,或者说有没有更好的方法来处理这个问题。答案可能很明显,但我刚来这里,脑袋现在有点卡住了:我有一个日志文件,内容大致是这样的,我想找出所有相同ID的行,这样我就可以比较这些匹配ID的值。我能找到第一行的匹配,但之后我的循环似乎就停止了。我不太确定自己哪里出错了,或者有没有更好的方法。任何帮助都非常感谢!

一些说明:

  • 当我拆分行的时候,XYZ ID这一列在line[2]的位置,且len(line)等于11。
  • 我想遍历整个文件,对于每一行,创建一个内部循环,扫描文件中剩下的行来寻找“匹配”。
  • 如果找到匹配,我想返回这个结果,以便我可以比较值。
  • 问题是我的代码在找到第一个匹配后似乎就停止了,因此只返回了第一个找到的匹配。

下面是我的代码和我正在处理的日志文件的一个示例(为了保护一些商业数据,我编辑了一些字符串)。实际的日志文件中包含逗号,但我在粘贴到这个论坛之前已经去掉了:

f = open('t.log','r')
for line in f:
    aline = line.replace(',','').split()
    if len(aline)==11:
        for line in f:
            bline = line.replace(',','').split()
            if len(bline)==11 and aline[2]==bline[2]:
                print 'a: ', aline
                print 'b: ', bline

#t.log

[13:40:19.xxx009] status    -------             
[13:40:19.xxx013] status    XYZ -4  -5675.36     quote  449.70/- 449.78 avg 1418.84 -7474.48       0.134     -55.630    -395.148    
[13:40:19.xxx021] status    XYZ  ID:22P00935xxx -4  3.92     quote:    0.98/   1.02  avg:   -0.98   -0.16
[13:40:19.xxx024] status    XYZ  ID:22C0099xxx0 -2  26.4     quote:   11.60/  11.85  avg:  -13.20    2.70
[13:40:19.xxx027] status    XYZ  ID:22P0099xxx0 10  -17.18   quote:    1.86/   1.90  avg:   -1.72    1.42
[13:40:19.xxx029] status    XYZ  ID:22C00995xxx 4   -42.5    quote:    8.20/   8.30  avg:  -10.62   -9.70
[13:40:19.xxx031] status    XYZ  ID:22P00995xxx 2   9.66     quote:    3.30/   3.40  avg:    4.83   16.26
[13:40:19.xxx535] status    total xx5.52                

[13:41:20.xxx688] status    -------             
[13:41:20.xxx691] status    XYZ -4  -5675.36     quote  449.83/- 449.99 avg 1418.84 -7475.32      -0.374    -213.006     -39.391    
[13:41:20.xxx701] status    XYZ  ID:22P00935xxx -4  3.92     quote:    0.96/   1.00  avg:   -0.98   -0.08
[13:41:20.xxx704] status    XYZ  ID:22C0099xxx0 -2  26.4     quote:   11.65/  11.90  avg:  -13.20    2.60
[13:41:20.xxx708] status    XYZ  ID:22P0099xxx0 10  -17.18   quote:    1.83/   1.87  avg:   -1.72    1.12
[13:41:20.xxx712] status    XYZ  ID:22C00995xxx 4   -42.5    quote:    8.20/   8.30  avg:  -10.62   -9.70
[13:41:20.xxx716] status    XYZ  ID:22P00995xxx 2   9.66     quote:    3.30/   3.35  avg:    4.83   16.26
[13:41:20.xxx718] status    XYZ  ID:22C0095xxx0 -10 35.6     quote:    5.40/   5.50  avg:   -3.56  -19.40
[13:41:20.001362] status    total xx6.68    

Result:    
$ python pnlcomp.py
    a:  ['[13:40:19.000021]', 'statusAAPL', '130322P00435000', '-4', '3.92', 'quote:', '0.98/', '1.02', 'avg:', '-0.98', '-0.16']
    b:  ['[13:41:20.000701]', 'statusAAPL', '130322P00435000', '-4', '3.92', 'quote:', '0.96/', '1.00', 'avg:', '-0.98', '-0.08']

3 个回答

0

这可能不是解决你问题的最佳方法,但如果你想知道怎么让它工作,可以看看:

这里的问题是,你的内层 for line in f: 循环会把文件的内容全部读取完,所以当你回到外层循环时,就没有东西可以读取了。(还有第二个问题:当我用你的数据运行代码时,len(aline) 总是 12,而不是 11。不过这个问题很简单就能解决。)

这并不是文件特有的情况;这是Python中所有迭代器的工作方式。处理任何迭代器有两种通用的方法,还有一种是专门针对文件的解决方案。

首先,有一个叫 itertools.tee 的东西。它接受一个迭代器,并返回 两个 迭代器,每个迭代器都可以独立前进。其实它在后台需要一些存储空间来处理不同步的情况,这就是文档中提到的:

一般来说,如果一个迭代器在另一个迭代器开始之前使用了大部分或全部数据,使用 list() 会比 tee() 更快。

这就是另一种选择:把整个迭代器读入一个 list,这样你就可以对切片进行循环。

显然,这种情况就是一个迭代器使用了大部分数据,而另一个迭代器在那儿等着。例如,在内层循环的第一次运行中,你会读取第1到第20000行,然后外层循环才读取第1行。所以,在这里使用 list 是更好的选择。因此:

f = open('t.log','r')
contents = list(f)
f.close()
for idx, line in enumerate(contents):
    aline = line.replace(',','').split()
    if len(aline)==11:
        for line in contents[idx+1:]:
            bline = line.replace(',','').split()
            if len(bline)==11 and aline[2]==bline[2]:
                print 'a: ', aline
                print 'b: ', bline

最后,如果你有一个可以进行检查点和恢复的高级迭代器,你可以在内层循环之前设置一个检查点,然后在内层循环之后恢复它。幸运的是,文件恰好有这样的功能: tell 可以返回当前文件的位置,而 seek 可以跳转到指定位置。(有一个大警告说“如果文件是以文本模式打开的(没有 'b'),那么只有 tell() 返回的偏移量是合法的。”但这没关系;你在这里只使用 tell 返回的偏移量。)

所以:

f = open('t.log','r')
for line in f:
    aline = line.replace(',','').split()
    if len(aline)==11:
        pos = f.tell()
        for line in f:
            bline = line.replace(',','').split()
            if len(bline)==11 and aline[2]==bline[2]:
                print 'a: ', aline
                print 'b: ', bline
        f.seek(pos)
1

你可以使用过滤功能来获取任何包含“ID”的行。

file = open('t.log', 'r')
result = filter(lambda s: "ID" in s, file)

你也可以用列表推导式来实现这个功能:

file = open('t.log', 'r')
result = [s for s in file if 'ID' in s]
1

你可能需要使用正则表达式(也叫做regex)来解决这个问题。Python有一个叫做re的模块,可以用来处理正则表达式。

这里有一个例子,可以帮助你理解方向:stackoverflow上关于在字符串中查找多个匹配项的问题.

上面的内容摘录如下:

日志文件的样子是:

[1242248375] SERVICE ALERT: myhostname.com;DNS: Recursive;CRITICAL

正则表达式的样子是:

regexp = re.compile(r'\[(\d+)\] SERVICE NOTIFICATION: (.+)')

它的结构是这样的:

  • r => 原始字符串(在正则表达式中总是推荐使用)
  • \[ => 匹配方括号(如果不这样写,它会被当作特殊字符)
  • (\d+) => 匹配一个或多个数字,\d表示数字,+表示一个或多个
  • \] => 后面跟着一个闭合的方括号
  • SERVICE NOTIFICATION: => 精确匹配这些字符的顺序。
  • (.+) => 点号(.)可以匹配任何字符,+同样表示一个或多个

括号用于分组结果。

我写了一个简单的正则表达式,来适应你的日志文件格式。假设你上面的日志保存在log.txt中。

import re
regexp = re.compile(r'\[(\d{2}:\d{2}:\d{2}\.xxx\d{3})\][\s]+status[\s]+XYZ[\s]+ID:([0-9A-Zx]+)(.+)')

f = open("log.txt", "r")
for line in f.readlines():
    print line
    m = re.match(regexp, line)
    #print m
    if m:
        print m.groups()

正则表达式乍一看可能不太容易理解,但如果你搜索“regex”或“re AND python”,你会找到很多有用的例子。

这段代码会给我输出:

[13:40:19.xxx021] status    XYZ  ID:22P00935xxx -4  3.92     quote:    0.98/   1.02  avg:   -0.98   -0.16

('13:40:19.xxx021', '22P00935xxx', ' -4  3.92     quote:    0.98/   1.02  avg:   -0.98   -0.16')
[13:40:19.xxx024] status    XYZ  ID:22C0099xxx0 -2  26.4     quote:   11.60/  11.85  avg:  -13.20    2.70

('13:40:19.xxx024', '22C0099xxx0', ' -2  26.4     quote:   11.60/  11.85  avg:  -13.20    2.70')
[13:40:19.xxx027] status    XYZ  ID:22P0099xxx0 10  -17.18   quote:    1.86/   1.90  avg:   -1.72    1.42

('13:40:19.xxx027', '22P0099xxx0', ' 10  -17.18   quote:    1.86/   1.90  avg:   -1.72    1.42')
[13:40:19.xxx029] status    XYZ  ID:22C00995xxx 4   -42.5    quote:    8.20/   8.30  avg:  -10.62   -9.70

('13:40:19.xxx029', '22C00995xxx', ' 4   -42.5    quote:    8.20/   8.30  avg:  -10.62   -9.70')
[13:40:19.xxx031] status    XYZ  ID:22P00995xxx 2   9.66     quote:    3.30/   3.40  avg:    4.83   16.26
('13:40:19.xxx031', '22P00995xxx', ' 2   9.66     quote:    3.30/   3.40  avg:    4.83   16.26')

每隔一行就是输出,包含了匹配到的组。

如果你把这个加到上面的程序中:

print "ID is : ", m.groups()[1]

输出结果是:

[13:40:19.xxx021] status    XYZ  ID:22P00935xxx -4  3.92     quote:    0.98/   1.02  avg:   -0.98   -0.16

ID is :  22P00935xxx

[13:40:19.xxx024] status    XYZ  ID:22C0099xxx0 -2  26.4     quote:   11.60/  11.85  avg:  -13.20    2.70

ID is :  22C0099xxx0

这会匹配你想要比较的ID。你可以稍微调整一下,得到你真正想要的结果。

最后的例子 会捕获ID,检查它是否已经存在,并将匹配的行添加到一个以ID为键的字典中:

import re regexp = re.compile(r'[(\d{2}:\d{2}:\d{2}.xxx\d{3})][\s]+status[\s]+XYZ[\s]+ID:([0-9A-Zx]+)(.+)')

res = {}

f = open("log.txt", "r")
for line in f.readlines():
    print line
    m = re.match(regexp, line)  
    if m:
        print m.groups()
        id = m.groups()[1]
        if id in res:
            #print "added to existing ID"
            res[id].append([m.groups()[0], m.groups()[2]])
        else:
            #print "new ID"
            res[id] = [m.groups()[0], m.groups()[2]]

for id in res:
    print "ID: ", id
    print res[id]

现在你可以随意尝试,调整它以适应你的需求。

撰写回答