返回日志文件中所有匹配的行
我需要一些关于循环的帮助,或者说有没有更好的方法来处理这个问题。答案可能很明显,但我刚来这里,脑袋现在有点卡住了:我有一个日志文件,内容大致是这样的,我想找出所有相同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 个回答
这可能不是解决你问题的最佳方法,但如果你想知道怎么让它工作,可以看看:
这里的问题是,你的内层 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)
你可以使用过滤功能来获取任何包含“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]
你可能需要使用正则表达式(也叫做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]
现在你可以随意尝试,调整它以适应你的需求。