在os.rename()后更新文件描述符
在调用 os.rename()
之后,处理 Bad file descriptor
错误的最佳方法是什么?
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 个回答
os.rename() 这个函数只根据文件的名字来工作,它并不知道有没有其他地方正在使用这个文件。所以在你对文件进行操作后,不能指望之前打开的文件对象还能正常使用。因此,最好在操作完成后把文件对象关闭,这样做是比较正确的选择。
你可以写一个函数来重命名一个打开的文件。基本上,这个过程是先关闭文件,然后重命名,再重新打开文件,同时保留一些属性,比如文件的位置和模式。不过在重新打开文件的时候,模式需要稍微调整一下。如果文件的模式是“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
对象已经不同了。我就把这个留给你自己去练习吧。 :-)