How to solve "OSError: telling position disabled by next() call"

How to solve "OSError: telling position disabled by next() call"

我正在创建一个文件编辑系统,我想创建一个基于行的 tell() 函数,而不是一个基于字节的函数。此函数将在 "with loop" 内部与 open(file) 调用一起使用。此函数是 class 的一部分,它具有:

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

以下为原函数 (如果你想要行和字节 return,它也有一个字符设置):

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

我遇到的问题是这个 return 是一个 OSError,它与系统如何遍历文件有关,但我不明白这个问题。感谢任何能提供帮助的人。

我有一个旧版本的 Python 3,我在 Linux 而不是 Mac,但我能够重新创建一些非常接近你的错误的东西:

IOError: telling position disabled by next() call

一个 IO 错误,不是 OS 错误,但其他方面相同。奇怪的是,我无法使用您的 open('a+', ...) 导致它,但只有在以读取模式打开文件时才会导致它:open('r+', ...).

更令人困惑的是,错误来自 _io.TextIOWrapper,一个 class 出现 被定义在 Python 的 _pyio.py 文件...我强调"appears",因为:

  1. 该文件中的 TextIOWrapper 具有类似 _telling 的属性,我无法在调用自身 _io.TextIOWrapper 的对象上访问这些属性。

  2. _pyio.py 中的 TextIOWrapper class 不区分可读、可写或随机访问文件。两者都应该起作用,或者两者都应该引发相同的 IOError.

无论如何,_pyio.py 文件中描述的 TextIOWrapper class 在迭代过程中禁用 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 的方法是 flush 方法,但如果在迭代进行时调用它也会失败:

IOError: can't reconstruct logical file position

至少在我的系统上,解决这个问题的方法是 TextIOWrapper 上调用 seek(0),这会将所有内容恢复到已知状态(并在讨价还价中成功调用 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:您应该考虑始终 return 同时考虑行号和字符偏移量。可以 return 完全不同类型的函数很难处理 --- 调用者更容易丢弃她或她不需要的值。

我不知道这是否是最初的错误,但如果您尝试在文件的 line-by-line 迭代中调用 f.tell(),您可能会遇到相同的错误,如下所示:

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 字符串很容易。

我有同样的错误:OSError: telling position disabled by next() call,并通过在打开文件时添加 'rb' 模式解决了这个问题。