Есть ли способ вернуться назад при чтении файла с помощью поиска и вызовов next()?

Я пишу скрипт Python для чтения файла, и когда я дохожу до раздела файла, окончательный способ чтения этих строк в разделе зависит от информации, которая также указана в этом разделе. Итак, я нашел здесь что я мог бы использовать что-то вроде

fp = open('myfile')
last_pos = fp.tell()
line = fp.readline()
while line != '':
  if line == 'SPECIAL':
  fp.seek(last_pos)
  other_function(fp)
  break
last_pos = fp.tell()
line = fp.readline()

Тем не менее, структура моего текущего кода выглядит примерно так:

fh = open(filename)

# get generator function and attach None at the end to stop iteration
items = itertools.chain(((lino,line) for lino, line in enumerate(fh, start=1)), (None,))
item = True

  lino, line = next(items)

  # handle special section
  if line.startswith['SPECIAL']:

    start = fh.tell()

    for i in range(specialLines):
      lino, eline = next(items)
      # etc. get the special data I need here

    # try to set the pointer to start to reread the special section  
    fh.seek(start)

    # then reread the special section

Но этот подход дает следующую ошибку:

указание позиции отключено вызовом next()

Есть ли способ предотвратить это?


person aaragon    schedule 27.03.2014    source источник


Ответы (2)


Использование файла в качестве итератора (например, вызов next() для него или использование его в цикле for) использует внутренний буфер; фактическая позиция чтения файла находится дальше по файлу, и использование .tell() не даст вам позицию следующей строки для выхода.

Если вам нужно искать туда и обратно, решение состоит не в том, чтобы использовать next() непосредственно в файловом объекте, а использовать только file.readline(). Вы все еще можете использовать итератор для этого, используйте версию iter() с двумя аргументами:

fileobj = open(filename)
fh = iter(fileobj.readline, '')

Вызов next() для fileiterator() будет вызывать fileobj.readline() до тех пор, пока эта функция не вернет пустую строку. По сути, это создает файловый итератор, который не использует внутренний буфер.

Демо:

>>> fh = open('example.txt')
>>> fhiter = iter(fh.readline, '')
>>> next(fhiter)
'foo spam eggs\n'
>>> fh.tell()
14
>>> fh.seek(0)
0
>>> next(fhiter)
'foo spam eggs\n'

Обратите внимание, что ваша цепочка enumerate может быть упрощена до:

items = itertools.chain(enumerate(fh, start=1), (None,))

хотя я в неведении, почему вы думаете, что здесь нужен (None,) часовой; StopIteration все равно будет повышен, хотя еще один вызов next() позже.

Чтобы прочитать specialLines строк счета, используйте itertools.islice():

for lino, eline in islice(items, specialLines):
    # etc. get the special data I need here

Вы можете просто зациклиться непосредственно на fh вместо использования бесконечного цикла, и next() тоже вызывает здесь:

with open(filename) as fh:
    enumerated = enumerate(iter(fileobj.readline, ''), start=1):
    for lino, line in enumerated:
        # handle special section
        if line.startswith['SPECIAL']:
            start = fh.tell()

            for lino, eline in islice(items, specialLines):
                # etc. get the special data I need here

            fh.seek(start)

но обратите внимание, что ваши номера строк будут увеличиваться, даже если вы вернетесь назад!

Однако вы, вероятно, захотите реорганизовать свой код, чтобы не нужно было перечитывать разделы вашего файла.

person Martijn Pieters    schedule 27.03.2014
comment
Спасибо @Martijn. Что происходит с вызовом перечислителя для получения номера строки? - person aaragon; 27.03.2014
comment
@AlejandroMarcosAragon: Ваше использование chain() немного... странно, но это сработает. - person Martijn Pieters; 27.03.2014
comment
Мне пришлось добавить None в конце, иначе у меня было исключение StopIteration, когда я достигал конца файла. Я до сих пор не могу заставить iter взять перечисление для линолеума. - person aaragon; 27.03.2014
comment
@AlejandroMarcosAragon: вы можете попросить next() вместо этого вернуть None, когда дойдете до конца; next(items, None). Похоже, у вас есть ошибка «отклонение на один», поскольку все, что вы сделали, это отложили StopIteration еще на один вызов. - person Martijn Pieters; 27.03.2014
comment
Хорошо, это то, что у меня есть до сих пор. Я пробовал: fit = enumerate(fh, start=1) try: while True: lino, line = next(fit, None), но это дает мне TypeError: 'NoneType' object is not iterable. Поэтому я заключил вызов next в попытке разорвать цикл. Могу ли я улучшить его дальше? - person aaragon; 27.03.2014
comment
Я все еще получаю сообщение об ошибке telling position disabled by next() call при внесении изменений, и теперь я использую итератор. - person aaragon; 27.03.2014
comment
@AlejandroMarcosAragon: убедитесь, что вы не вызываете next() для фактического дескриптора файла где-то еще, только используете объект iter(fileobj.readline, '') для вызова next() и вызываете tell() для объекта развернутого файла. - person Martijn Pieters; 27.03.2014
comment
Во входном файле каждая строка специального раздела определяет объект определенного типа. Поэтому мне нужно знать к концу специального раздела, сколько объектов какого типа у меня есть, чтобы создать словарь, чтобы я мог позже построить массивы numpy нужного размера. - person aaragon; 27.03.2014
comment
@AlejandroMarcosAragon: Тогда почему бы не отследить это где-нибудь в счетчике и/или не сохранить уже проанализированные объекты? - person Martijn Pieters; 27.03.2014
comment
Но разве это не менее эффективно? Огромный файл может содержать миллионы таких строк. - person aaragon; 27.03.2014
comment
Звучит так, как будто вы уже храните информацию где-то в памяти. - person Martijn Pieters; 27.03.2014
comment
Я сделаю тест, я создам очень большой входной файл, и я попытаюсь прочитать его в обоих направлениях, чтобы проверить разницу в скорости, и я дам вам знать. - person aaragon; 27.03.2014
comment
Итак, я провел тест в обоих случаях, в другом случае я использую io.StringIO для записи файла, который я уже прочитал. Оказывается, подход поиска быстрее (1 мин 16,911 с по сравнению с 1 мин 28,933 с для 3973781 из этих строк). - person aaragon; 27.03.2014
comment
Я не хотел, чтобы вы использовали объект файла в памяти. Мне непонятно, почему вам нужно каждый раз дважды анализировать один и тот же фрагмент текста. - person Martijn Pieters; 27.03.2014
comment
Я объясню лучше. Этот раздел содержит столько строк, сколько элементов (объектов, которые необходимо создать). Но информация об этих объектах хранится в массивах numpy. В тот момент, когда я начинаю синтаксический анализ в первый раз, я понятия не имею, сколько типов различных элементов у меня есть и сколько элементов для каждого типа, поэтому я не могу заранее создать массивы numpy. Итак, к концу первого прохода, поскольку у меня есть эта информация, я создаю массивы. Во втором проходе я присваиваю данные элементов массивам. - person aaragon; 27.03.2014
comment
@AlejandroMarcosAragon: и вы не можете сначала поместить данные, которые вы поместите в массивы, в списки Python? - person Martijn Pieters; 27.03.2014
comment
Я мог бы, но будет ли это более эффективно, чем то, что я сделал с SringIO? - person aaragon; 27.03.2014
comment
Я не знаю, я не знаю ваших данных. - person Martijn Pieters; 27.03.2014
comment
Меньше циклов, меньше ввода-вывода. - person Martijn Pieters; 27.03.2014

Я не эксперт по версии 3 Python, но кажется, что вы читаете, используя generator те yields строки, которые считываются из файла. Таким образом, вы можете иметь только одностороннее направление.

Вам придется использовать другой подход.

person Andrew Dunai    schedule 27.03.2014