【问题标题】:Python file.tell gives wrong value locationPython file.tell 给出了错误的值位置
【发布时间】:2013-11-01 16:19:25
【问题描述】:

我正在尝试使用 Python 从现有文件中提取多个位置。这是我当前用于提取位置的代码:

    self.fh = open( fileName , "r+")
    p = re.compile('regGen regPorSnip begin')
    for line in self.fh :
        if ( p.search(line) ):
            self.porSnipStartFPtr = self.fh.tell()
            sys.stdout.write("found regPorSnip")

这个 sn-p 使用不同的搜索值重复多次(打开文件较少),并且似乎有效:我得到了正确的消息,并且变量具有值。

但是,使用下面的代码,第一个写入位置是错误的,而后面的写入位置是正确的:

    self.fh.seek(self.rstSnipStartFPtr,0)
    self.fh.write(str);
    sys.stdout.write("writing %s" % str )
    self.rstSnipStartFPtr = self.fh.tell()

我已经读过,将某些 read/readline 选项传递给 fh 可能会导致错误的告诉值,因为 Python 倾向于“预读”。我看到的一个避免这种情况的建议是读取整个文件并重写它,这在我的应用程序中不是一个非常吸引人的解决方案。

如果我将第一个代码 sn-p 更改为:

  for line in self.fh.read() :
        if ( p.search(line) ):
            self.porSnipStartFPtr = self.fh.tell()
            sys.stdout.write("found regPorSnip")

然后,self.fh.read() 似乎只返回字符而不是整行。搜索永远不会匹配。 self.fh.readline() 似乎也是如此。

我的结论是fh.tell 仅在写入操作后查询时返回有效的文件位置。

有没有办法在读取/搜索时提取准确的文件位置?

谢谢。

【问题讨论】:

标签: python seek tell


【解决方案1】:

文件对象的next() 方法的文档中解释了原因(相当模糊):

当文件用作迭代器时,通常在 for 循环中(例如, 对于 f 中的行:打印行),重复调用 next() 方法。 此方法返回下一个输入行,或在以下情况下引发 StopIteration EOF 被击中。为了使for循环成为最有效的循环方式 在文件的行上(一个非常常见的操作), next() 方法 使用隐藏的预读缓冲区。由于使用预读 缓冲区,将 next() 与其他文件方法(如 readline())结合使用 不能正常工作。但是,使用 seek() 将文件重新定位到 绝对位置将刷新预读缓冲区。

tell() 返回的值反映了这个隐藏的预读缓冲区已经到达了多远,通常比程序实际检索到的字符多出几千字节。

没有可移植的方法解决这个问题。如果您需要将tell() 与读取行混合,请改用文件的readline() 方法。权衡是,为了获得可用的tell() 结果,使用readline() 迭代一个大文件通常比使用for line in file_object: 慢得多。

代码

具体来说,把循环改成这样:

line = self.fh.readline()
while line:
    if p.search(line):
        self.porSnipStartFPtr = self.fh.tell()
        sys.stdout.write("found regPorSnip")
    line = fh.readline()

我不确定这是否是您真正想要的,不过:tell() 正在捕获 next 行开头的位置。如果想要行的 start 的位置,则需要更改逻辑,如下所示:

pos = self.fh.tell()
line = self.fh.readline()
while line:
    if p.search(line):
        self.porSnipStartFPtr = pos
        sys.stdout.write("found regPorSnip")
    pos = self.fh.tell()
    line = fh.readline()

或者用“循环半”来做:

while True:
    pos = self.fh.tell()
    line = self.fh.readline()
    if not line:
        break
    if p.search(line):
        self.porSnipStartFPtr = pos
        sys.stdout.write("found regPorSnip")

【讨论】:

  • 文件不是很大,所以我认为使用 readline 的惩罚不会成为问题。第一个选项是最合适的选项。下一行的开头是好的。当需要文件指针时,似乎无法在读取行时测试空文件。感谢您的澄清。非常感谢。
  • 非常感谢的解释!我还发现这个问题处理一个大文件,但通过手动保留一个偏移变量(offset += len(line))而不是调用 fh.tell()来解决它。这样您就可以保留 next() 中包含的优化
  • @duglon,应该可以在 Linuxy 系统上运行,但在 Windows 中文本模式文件的tell() 结果通常不是文件中的简单字节偏移量。 Python从C继承了这个限制。这就是为什么文档说“在文本文件中(那些在模式字符串中没有ab打开的文件),只允许相对于文件的开头进行搜索(例外是用seek搜索到文件末尾(0, 2)),唯一有效的偏移值是那些从 f.tell() 或零返回的值。任何其他偏移值都会产生未定义的行为。"
  • 谢谢蒂姆!我忘了添加那篇文章 - 我以 mode="rb" 开头
【解决方案2】:

我想我不明白这个问题

>>> fh = open('test.txt')
>>> fh.tell()
0L
>>> fh.read(1)
'"'
>>> fh.tell()
1L
>>> fh.read(5)
'a" \n"'
>>> fh.tell()
7L

【讨论】:

  • 问题实际上是由于使用for line in file_object: - 然后还有另一层缓冲。
  • 啊,明白了……好吧,我会删除这个
  • 为什么for line in file_object(pythonic 方式)是个问题?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多