Python: перематывание одной строки в файле при итерации с помощью f.next ()

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 ()?

10
задан Community 23 May 2017 в 12:34
поделиться

3 ответа

Нет.Я бы сделал адаптер, который в основном перенаправлял все вызовы, но сохранял копию последней строки, когда вы выполняли 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()

Это было бы несложно расширить до чего-то, что позволяло бы выполнять резервное копирование более чем на одно значение.

12
ответ дан 3 December 2019 в 20:39
поделиться

Итератор файла Python выполняет много буферизации, тем самым продвигая позицию в файле намного раньше, чем ваша итерация. Если вы хотите использовать file.tell () , вы должны сделать это «по-старому»:

with open(filename) as fileob:
  line = fileob.readline()
  while line:
    print fileob.tell()
    line = fileob.readline()
1
ответ дан 3 December 2019 в 20:39
поделиться

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):
    ...
5
ответ дан 3 December 2019 в 20:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: