从大日志文件的某个偏移位置开始读取若干行
2011-06-03
操作大日志文件时,经常需要从日志文件的上次处理到的位置开始,读取若干行进行处理,并记下当前位置以便下一次处理。同时,日志文件可能仍在变化中,新的记录可能正在不时地追加到文件末尾。
如果要处理的日志文件是一个静态的文件,不会再有内容追加进来,并且也不需要记下当前处理到的位置,那么代码很简单(以下代码都是 Python 代码):
f = open("bigfile.log", "rb")
for ln in f:
deal(ln)
f.close()
如果同时要记下当前处理到的文件偏移(位置),这种方法就有一点问题了,比如:
f = open("bigfile.log", "rb")
f.seek(10000) # 从位置 10000 的地方开始处理
for ln in f:
deal(ln)
print f.tell() # 注意这个值
f.close()
在我的测试中,上面的代码中的 f.tell()
返回的始终是 18192,显然,这个数字是 seek
指定的初始偏移 10000 加上默认的 bufsize 8192 的值。搜索了一下,大致的解释是这样:文件对象 f
作为迭代器时,它会根据 bufsize 的值(例如 8k 或 10k)一次性读入一大块内容,再每次返回这一大块内容中的一行。既然已经读入了一大块内容,文件的指针当然已经移到那一大块内容结束的地方,所以每次循环时 f.tell()
的值都是这个数字。当然,如果文件很大,这一大块内容处理完后后面还有内容,程序就会再往后读一大块内容,对应地,f.tell()
将返回一个新的值。
这样的文件偏移显然不符合我们的需求。为了能得到准确的文件偏移,代码要改成这样:
f = open("bigfile.log", "rb")
f.seek(10000) # 从位置 10000 的地方开始处理
while True:
ln = f.readline()
if ln == "":
break
print f.tell() # 注意这个值
f.close()
使用这样的方式,f.tell()
就能返回准确的文件偏移了。
再把它稍微包装一下,写成一个迭代器,如下:
def yieldLine(log_fn, start=0):
u"迭代器,返回 log_fn 从 start 位置开始的下一行及新的文件偏移"
if not os.path.isfile(log_fn):
raise StopIteration
f = open(log_fn, "rb")
f.seek(start)
while True:
ln = f.readline()
if ln == "":
f.close()
raise StopIteration
yield ln, f.tell()
# 用法:
yl = yieldLine("bigfile.log", start=1000)
for ln, pos in yl:
deal(ln)
print pos
当然,如果在读文件的同时文件末尾也在不断地追加,最后一次读取时有可能会只读到半行内容。这时,需要判断一下读取的内容是否完整,如果不完整就先缓存起来,和下次读到的内容合并之后再处理。
前一篇关于热区图的色盘
发表评论:
电子邮件地址不会被公开。必填项已用 * 标注。
评论:
hi oldj...
谢谢!(Python新手刚开始学习:P)
1:根据你的需要啊,如果你的脚本会先退出,将来再次启动时继续执行,那就记录到磁盘上,如果你的脚本一直在运行,只是会sleep一段时间,那直接记在某个内存变量中就可以了。
2:这个需要根据你的文件格式来判断,比如如果你的文件是日志类型的,每一行都可以用“t”分割为9列,并且最后一列能匹配某个正则,那么你就可以测试一下你读到的最后一行是否满足这个条件。当然,如果你的文件更新很快,你也可以总是把最后一行缓存起来,留到下一次读取时与后面的内容一起处理。