Как решить "OSError: указание позиции отключено по вызову next ()"

Я создаю систему редактирования файлов и хотел бы сделать функцию tell() на основе строки вместо байтовой. Эта функция будет использоваться внутри цикла "with" с вызовом open(file). Эта функция является частью класса, который имеет:

self.f = open(self.file, 'a+')
# self.file is a string that has the filename in it

ниже исходной функции (Он также имеет параметр char, если вы хотите вернуть строку и Байт):

def tell(self, char=False):
    t, lc = self.f.tell(), 0
    self.f.seek(0)
    for line in self.f:
        if t >= len(line):
            t -= len(line)
            lc += 1
        else:
            break
    if char:
        return lc, t
    return lc

проблема, с которой я сталкиваюсь, заключается в том, что это возвращает OSError и связано с тем, как система повторяет файл, но я не понимаю проблему. Спасибо всем, кто может помочь.

3 ответов


у меня есть более старая версия Python 3, и я на Linux вместо Mac, но мне удалось воссоздать что-то очень близкое к вашей ошибке:

IOError: telling position disabled by next() call

An IO ошибки, а не OS ошибки, но в остальном то же самое. Как ни странно, я не мог вызвать его с помощью open('a+', ...), но только при открытии файла в режиме чтения: open('r+', ...).

дальнейшая путаница заключается в том, что ошибка исходит от _io.TextIOWrapper, класс, появляется будет определен в ... Я подчеркиваю "появляется", потому что:

  1. на TextIOWrapper в этом файле есть такие атрибуты, как _telling что я не могу получить доступ к объекту whatever-it-is, называющему себя _io.TextIOWrapper.

  2. на TextIOWrapper класс _pyio.py не делает никакого различия между читаемыми, записываемыми или случайными файлами. Либо оба должны работать, либо оба должны поднять то же самое IOError.

независимо от того,TextIOWrapper класс, как описано в запрещает tell метод во время итерации. Кажется, это то, с чем вы сталкиваетесь (комментарии мои):

def __next__(self):
    # Disable the tell method.
    self._telling = False
    line = self.readline()
    if not line:
        # We've reached the end of the file...
        self._snapshot = None
        # ...so restore _telling to whatever it was.
        self._telling = self._seekable
        raise StopIteration
    return line

в своем tell метод, вы почти всегда break из итерации, прежде чем она достигнет конца файла, оставив _telling отключен (False):

еще один способ сброса _telling is the flush метод, но он также не удался при вызове во время итерации:

IOError: can't reconstruct logical file position

путь вокруг этого, по крайней мере на моей системе, это вызов seek(0) на TextIOWrapper, который восстанавливает все до известного состояния (и успешно вызывает flush в придачу):

def tell(self, char=False):
    t, lc = self.f.tell(), 0
    self.f.seek(0)
    for line in self.f:
        if t >= len(line):
            t -= len(line)
            lc += 1
        else:
            break
    # Reset the file iterator, or later calls to f.tell will
    # raise an IOError or OSError:
    f.seek(0)
    if char:
        return lc, t
    return lc

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

PS: вы должны рассмотреть всегда возврат номера строки и смещения символов. Функции, которые могут возвращать совершенно разные типы, трудно иметь дело с - - - намного проще для вызывающего абонента просто выбросить значение, которое ей или ей не нужно.


Я не знаю, была ли это первоначальная ошибка, но вы можете получить ту же ошибку, если попытаетесь вызвать f.tell () внутри строчной итерации файла типа so:

with open(path, "r+") as f:
  for line in f:
    f.tell() #OSError

который можно легко заменить следующим:

with open(path, mode) as f:
  line = f.readline()
  while line:
    f.tell() #returns the location of the next line
    line = f.readline()

просто быстрый обходной путь для этой проблемы:

как вы перебираете файл с самого начала в любом случае, просто отслеживать, где вы находитесь с выделенной переменной:

file_pos = 0
with open('file.txt', 'rb') as f:
    for line in f:
        # process line
        file_pos += len(line)

Теперь file_pos будет всегда, что file.tell() б рассказать вы. Обратите внимание, что это работает только для ASCII-файлов, так как tell и seek работают с байтовыми позициями. Работая на линейной основе, легко конвертировать строки из байта в строки unicode.