我现在需要处理配置文件。由于它的生成方式,它包含这样的行:
---(more 15%)---
第一步是剥离这些不需要的线。稍微扭曲一下,这些线条中的每一条都跟着一条空白线,我也想剥离它。我创建了一个快速Python脚本来执行此操作:
skip_next = False
for line in sys.stdin:
if skip_next:
skip_next = False
continue
if line.startswith('---(more'):
skip_next = True
continue
print line,
现在,这有效,但它比我希望的更黑。难点在于,当循环遍历行时,我们希望一行的内容影响后续行。因此我的问题是:一个循环迭代影响另一个循环迭代的优雅方式是什么?
答案 0 :(得分:11)
这让人觉得尴尬的原因是你从根本上做错了。 for
循环应该是对系列的每个元素的顺序迭代。如果你正在做一些调用continue
的东西,甚至没有查看当前元素,基于系列的前一个元素中发生的事情,你就打破了那个基本的抽象。然后,您需要使用额外的移动部件来处理尴尬,以便处理您正在设置的方形圆孔解决方案。
相反,请尝试将操作保持接近导致该操作的条件。我们知道for
循环只是while
循环的特殊情况的语法糖,所以让我们使用它。伪代码,因为我不熟悉Python的I / O子系统:
while not sys.stdin.eof: //or whatever
line = sys.stdin.ReadLine()
if line.startswith('---(more'):
sys.stdin.ReadLine() //read the next line and ignore it
continue
print line
答案 1 :(得分:7)
另一种方法是使用itertools.tee
,它允许您将迭代器拆分为两个。然后,您可以将一个迭代器前进一步,将一个迭代器放在另一个前面一行。然后,您可以压缩两个迭代器,并在for
循环的每一步查看前一行和当前行(我使用izip_longest
,因此它不会丢弃最后一行) :
from itertools import tee, izip_longest
in1, in2 = tee(sys.stdin, 2)
next(in2)
for line, prevline in izip_longest(in1, in2, fillvalue=''):
if line.startswith('---(more') or prevline.startswith('---(more'):
continue
print line
这也可以作为等效的生成器表达式来完成:
from itertools import tee, izip_longest
in1, in2 = tee(sys.stdin, 2)
next(in2)
pairs = izip_longest(in1, in2, fillvalue='')
res = (line for line, prevline in pairs
if not line.startswith('---(more') and not prevline.startswith('---(more'))
for line in res:
print line
或者您可以使用filter
,它允许您在条件不为真时删除迭代器项。
from itertools import tee, izip_longest
in1, in2 = tee(sys.stdin, 2)
next(in2)
pairs = izip_longest(in1, in2, fillvalue='')
cond = lambda pair: not pair[0].startswith('---(more') and not pair[1].startswith('---(more')
res = filter(cond, pairs)
for line in res:
print line
如果您愿意访问python标准库,toolz
包使这更容易。它提供了sliding_window
函数,允许您将a b c d e f
之类的迭代器拆分为(a,b), (b,c), (c,d), (d,e), (e,f)
之类的函数。这与上面的tee
方法基本相同,只是将三行合并为一个:
from toolz.itertoolz import sliding_window
for line, prevline in sliding_wind(2, sys.stdin):
if line.startswith('---(more') or prevline.startswith('---(more'):
continue
print line
你可以另外使用remove
,基本上与filter
相反,删除项目而不需要for
循环:
from tools.itertoolz import sliding_window, remove
pairs = sliding_window(2, sys.stdin)
cond = lambda x: x[0].startswith('---(more') or x[1].startswith('---(more')
res = remove(cond, pairs)
for line in res:
print line
答案 2 :(得分:0)
在这种情况下,我们可以通过手动推进迭代器来跳过一行。这导致代码有点类似于Mason Wheeler的解决方案,但仍然使用迭代语法。有一个相关的Stack Overflow question:
for line in sys.stdin:
if line.startswith('---(more'):
sys.stdin.next()
continue
print line,