Python: rewinding one line in file when iterating with f.next()
当您使用f.next()迭代文件时,python的f.tell不能像我预期的那样工作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | >>> f=open(".bash_profile","r") >>> f.tell() 0 >>> f.next() "alias rm='rm -i' " >>> f.tell() 397 >>> f.next() "alias cp='cp -i' " >>> f.tell() 397 >>> f.next() "alias mv='mv -i' " >>> f.tell() 397 |
看起来它给了您缓冲区的位置,而不是您刚用next()得到的位置。
我以前使用seek/tell技巧在使用readline()迭代文件时倒回一行。在使用next()时,是否有方法倒回一行?
不,我会制作一个适配器,它主要转发所有调用,但在执行
实际上,我将使适配器成为一个可以包装任何iterable的适配器,而不是包装文件的适配器,因为听起来它在其他上下文中通常很有用。
Alex关于使用
下面是一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | class rewindable_iterator(object): not_started = object() def __init__(self, iterator): self._iter = iter(iterator) self._use_save = False self._save = self.not_started def __iter__(self): return self def next(self): if self._use_save: self._use_save = False else: self._save = self._iter.next() return self._save def backup(self): if self._use_save: raise RuntimeError("Tried to backup more than one step.") elif self._save is self.not_started: raise RuntimeError("Can't backup past the beginning.") self._use_save = True fiter = rewindable_iterator(file('file.txt', 'r')) for line in fiter: result = process_line(line) if result is DoOver: fiter.backup() |
这不会太难扩展到允许您以一个以上的值进行备份的内容中。
itertools.tee可能是最不糟糕的方法——您不能通过对文件进行迭代来"击败"缓冲(您也不想这样做:性能影响会很糟糕),所以让两个迭代器(一个"落后"一个)对我来说似乎是最合理的解决方案。
1 2 3 4 5 6 7 | import itertools as it with open('a.txt') as f: f1, f2 = it.tee(f) f2 = it.chain([None], f2) for thisline, prevline in it.izip(f1, f2): ... |
python的文件迭代器做了大量的缓冲处理,从而使文件中的位置远远领先于迭代。如果你想用
1 2 3 4 5 | with open(filename) as fileob: line = fileob.readline() while line: print fileob.tell() line = fileob.readline() |