f.tell Python не работает так, как я ожидал, когда вы перебираете файл с помощью f.next ():
>>> 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
Похоже, он дает вам позицию буфера, а не позицию того, что Вы только что получили с next ().
Я ранее использовал трюк поиска / рассказать , чтобы перематывать одну строку при переборе файла с readline (). Есть ли способ перемотать одну строку при использовании next ()?
Нет.Я бы сделал адаптер, который в основном перенаправлял все вызовы, но сохранял копию последней строки, когда вы выполняли next
, а затем позволял бы вам вызвать другой метод, чтобы эта строка снова выскочила.
Я бы фактически сделал адаптер адаптером, который мог бы заключать в оболочку любой итерационный объект вместо оболочки для файла, потому что это звучит так, как будто это часто было бы полезно в других контекстах.
Предложение Алекса об использовании адаптера 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()
Это было бы несложно расширить до чего-то, что позволяло бы выполнять резервное копирование более чем на одно значение.
Итератор файла Python выполняет много буферизации, тем самым продвигая позицию в файле намного раньше, чем ваша итерация. Если вы хотите использовать file.tell ()
, вы должны сделать это «по-старому»:
with open(filename) as fileob:
line = fileob.readline()
while line:
print fileob.tell()
line = fileob.readline()
itertools.tee , вероятно, наименее плохой подход - вы можете » t «победить» буферизацию, выполняемую итерацией в файле (и вы бы не захотели: эффекты производительности были бы ужасными), поэтому сохранение двух итераторов, один «на шаг позади» другого, кажется мне самым разумным решением.
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):
...