在os.rename()之后更新文件描述符

时间:2011-07-14 18:12:35

标签: python

以下列方式致电Bad file descriptor后处理os.rename()错误的最佳方法是什么?

f = open('foo.txt', 'rw')
os.rename(f.name, f.name + ".bak")

在文件系统中,不再是foo.txt,而是foo.txt.bak

然而..

f.name

代替foo.txt

foo.txt.bak

但是...

f.write("test")

给出Bad file descriptor

有更好的方法来更新文件描述符吗?

即使文件已被重命名,我是否还应该致电f.close()

2 个答案:

答案 0 :(得分:4)

您可以编写一个重命名打开文件的函数。基本上,您关闭,重命名和重新打开文件,保留文件位置和模式等属性。重新打开模式的一些调整是必要的 - 如果文件的模式是“w”,以相同的模式重新打开它将丢失其中的所有内容,因此在重新打开时我们使用“r +”模式。 (这不是完美的,因为它允许对文件的读访问,这是以前没有的,但它是我们能做的最好的。)你当然会得到一个全新的file对象,这是函数的返回值。

import os

def rename_open_file(fileobj, newname):
    name = fileobj.name
    mode = fileobj.mode.lower()
    posn = fileobj.tell()
    fileobj.close()
    os.rename(name, newname)
    newmode = mode
    if "w" in mode:      # can't reopen with "w" mode since
        newmode = "r+"   # it would empty the file; use "r+"
        if "b" in mode:
           newmode += "b"
    fileobj = open(name, newmode)
    fileobj.seek(posn)
    return fileobj

f = rename_open_file(f, f.name + ".bak")

如果您有多个file对象引用打开的文件,当然这没有用;所有其他参考文献可能会破裂。

警告:文件的name属性不一定是完整路径,因此如果您使用相对路径打开文件并且在打开文件后更改了目录,则这不起作用。如果这是一个问题,您可以编写自己的open(),在开放时间(使用os.path.abspath())计算文件的完整路径名。

此外,不保留打开文件时给出的缓冲区大小,因为这不会记录在文件对象的任何位置。编写自己的open()也可以解决这个问题。最简单的方法是继承file

from os.path import abspath
class open(file):
    def __init__(self, filename, mode="r", buffering=-1):
        file.__init__(self, abspath(filename), mode, buffering)
        self.buffering = buffering

然后你可以为你的功能增加保持缓冲:

import os

def rename_open_file(fileobj, newname):
    name = fileobj.name
    mode = fileobj.mode.lower()
    posn = fileobj.tell()
    buff = fileobj.buffering
    fileobj.close()
    os.rename(name, newname)
    newmode = mode
    if "w" in mode:      # can't reopen with "w" mode since
        newmode = "r+"   # it would empty the file; use "r+"
        if "b" in mode:
           newmode += "b"
    fileobj = open(name, newmode, buff)
    fileobj.seek(posn)
    return fileobj

您还可以为文件对象编写包装器类,而不是为file创建子类,并让它通过对包装对象的所有file方法调用。然后rename()可以是包装器的方法,并执行上述所有操作。由于调用代码将保留对包装器的引用,因此不需要知道底层file对象是不同的。我将把它留作练习。 : - )

答案 1 :(得分:1)

os.rename()在纯文件名上工作,而不知道任何引用此文件的打开文件对象。因此,在对基础文件执行操作后,您不能依赖文件对象,因此关闭它可能是正确的做法。