`cat 文件名 | grep -B 5 -C 5 foo`
for filename in os.listdir("."):
for line in open(filename).xreadlines():
if "foo" in line:
print line
这段话的意思是,有一个简单的Python代码,可以实现类似于命令行中 cat filename | grep foo
的功能。不过,提问者想要的是一个更复杂的功能,类似于 cat filename | grep -B 5 -C 5 foo
,也就是在查找包含“foo”的行时,还想要显示它前面和后面各5行的内容。提问者想知道应该如何修改上面的代码才能实现这个功能。
2 个回答
1
虽然我喜欢Alex的回答简单明了,但在处理大文件时,这种方法会消耗很多内存。那么我们来看看这个算法怎么样?
import os
for filename in (f for f in os.listdir(".") if os.path.isfile(f)):
prevLines = []
followCount = 0
for line in open(filename):
prevLines.append(line)
if "foo" in line:
if followCount <= 0:
for prevLine in prevLines:
print prevLine.strip()
else:
print line.strip()
followCount = 5
elif followCount > 0:
print line.strip()
followCount -= 1
if len(prevLines) > 5:
prevLines.pop(0)
7
最简单的方法是:
for filename in os.listdir("."):
lines = open(filename).readlines()
for i, line in enumerate(lines):
if "foo" in line:
for x in lines[i-5 : i+6]:
print x,
可以根据需要添加行号、块之间的间隔等等;-)。
如果你碰到特别大的文本文件(比如说,比《圣经》大200-300倍的文件,而《圣经》的文本文件大约是4.3MB),我建议使用生成器来创建一个滑动窗口(也就是一个“先进先出”的行列表)。为了简单起见,这里只关注搜索文件中间的行,排除开头和结尾的几行(这需要额外的几个特殊处理的循环——所以我也返回索引...因为在这两个额外的循环中,索引不总是5!):
import collections
def sliding_windows(it):
fifo = collections.deque()
# prime the FIFO with the first 10
for i, line in enumerate(it):
fifo.append(line)
if i == 9: break
# keep yielding 11-line sliding-windows
for line in it:
fifo.append(line)
yield fifo, 5
fifo.popleft()
for w, i in sliding_windows(open(filename)):
if "foo" in w[i]:
for line in w: print line,
我想我会把这些特殊处理的循环(以及处理非常少行的文件的担忧;-)留给大家自己练习,因为这一切都非常假设性。
给你几个小提示...:最后的“特殊处理循环”其实很简单——就是不断丢掉第一行,显然不需要再添加,因为没有更多的内容可以添加了……索引应该始终是5,当你得到一个窗口,5是最后的索引(也就是文件的最后一行)时,你就完成了;起始情况稍微复杂一点,因为你不想在读取前6行之前就返回结果,这时索引会是0(文件的第一行)……
最后,作为额外的挑战,考虑一下如何让这个方法也适用于非常短的文件!-)