当你使用f.next()迭代文件时,Python的f.tell不能像我预期的那样工作:
>>> f=open(".bash_profile", "r")
>>> f.tell()
0
>>> f.next()
"alias rm='rm -i'\n"
>>> f.tell()
397
>>> f.next()
"alias cp='cp -i'\n"
>>> f.tell()
397
>>> f.next()
"alias mv='mv -i'\n"
>>> f.tell()
397
看起来它给你缓冲区的位置而不是你刚接下来的位置()。
我以前使用seek / tell trick在使用readline()迭代文件时回绕一行。使用next()时有没有办法倒回一行?
答案 0 :(得分:12)
没有。我会创建一个很大程度上转发所有调用的适配器,但是当你执行next
时保留最后一行的副本,然后让你调用另一种方法使该行再次弹出。
我实际上会使适配器成为一个可以包装任何可迭代而不是文件包装器的适配器,因为这听起来像在其他环境中经常有用。
Alex建议使用itertools.tee
适配器也有效,但我认为编写自己的迭代器适配器来处理这种情况通常会更清晰。
以下是一个例子:
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()
这不会太难以扩展到允许您备份超过一个值的东西。
答案 1 :(得分:5)
itertools.tee可能是最不好的方法 - 你不能通过迭代文件来“击败”缓冲(你也不想:性能影响会很糟糕),所以保持两个迭代器,一个“落后”另一个,对我来说似乎是最合适的解决方案。
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):
...
答案 2 :(得分:1)
Python的文件迭代器进行了大量的缓冲,从而在远离迭代之前提升了文件中的位置。如果你想使用file.tell()
,你必须“旧方式”:
with open(filename) as fileob:
line = fileob.readline()
while line:
print fileob.tell()
line = fileob.readline()