2012-04-27 56 views
31

我想:如何打开(读写)或创建允许截断的文件?

  • 如果它存在,以读写模式打开文件;
  • 如果不存在则创建它;
  • 能够随时随地截断它。

EDIT:与截断我的意思是写直到一个位置,并丢弃该文件的剩余部分,如果存在的话

所有这些原子(具有单个open()呼叫或模拟单个open()呼叫)

没有一个单一的开放模式似乎也适用:

  • R:显然是行不通的;
  • r +:如果文件不存在则失败;
  • w:重新创建文件,如果它存在;
  • w +:重新创建文件,如果它存在;
  • a:无法读取;
  • a +:不能截断。

我试过的一些组合(rw,rw +,r + w等)似乎也不起作用。可能吗?

从Ruby的一些doc(适用于蟒太):

r 
Read-only mode. The file pointer is placed at the beginning of the file. 
This is the default mode. 

r+ 
Read-write mode. The file pointer will be at the beginning of the file. 

w 
Write-only mode. Overwrites the file if the file exists. If the file 
does not exist, creates a new file for writing. 

w+ 
Read-write mode. Overwrites the existing file if the file exists. If the 
file does not exist, creates a new file for reading and writing. 

a 
Write-only mode. The file pointer is at the end of the file if the file 
exists. That is, the file is in the append mode. If the file does not exist, 
it creates a new file for writing. 

a+ 
Read and write mode. The file pointer is at the end of the file if the file 
exists. The file opens in the append mode. If the file does not exist, it 
creates a new file for reading and writing. 
+0

所以你基本上是要覆盖一个文件并确保以前的内容不会停留在您停止写入的位置之下? – 2012-04-27 12:14:28

+0

是的。 'file.truncate()'适用于这个目的,它在文件为“r +”,“w”,“w +”时起作用。但他们都有我上面列出的缺陷。 – ceztko 2012-04-27 12:21:21

回答

28

根据OpenGroup

O_TRUNC

如果该文件存在并且是一个普通文件,并且该文件被成功 打开O_RDWR或O_WRONLY,它的长度被截断为0和模式 和所有者不变。它不会影响FIFO特殊文件 或终端设备文件。它对其他文件类型的影响是依赖于实现的 。使用O_TRUNC和O_RDONLY的结果是未定义的 。

因此,打开带有“w”或“w +”的文件时可能会传递O_TRUNC。这给“截断”一个不同的含义,不是我想要的。

与python解决方案似乎打开文件在低级别的I/O与os.open()函数。

下面的Python功能:

def touchopen(filename, *args, **kwargs): 
    # Open the file in R/W and create if it doesn't exist. *Don't* pass O_TRUNC 
    fd = os.open(filename, os.O_RDWR | os.O_CREAT) 

    # Encapsulate the low-level file descriptor in a python file object 
    return os.fdopen(fd, *args, **kwargs) 

有我想要的行为。您可以使用它像这样(它其实我的使用情况):

# Open an existing file or create if it doesn't exist 
with touchopen("./tool.run", "r+") as doing_fd: 

    # Acquire a non-blocking exclusive lock 
    fcntl.lockf(doing_fd, fcntl.LOCK_EX) 

    # Read a previous value if present 
    previous_value = doing_fd.read() 
    print previous_value 

    # Write the new value and truncate 
    doing_fd.seek(0) 
    doing_fd.write("new value") 
    doing_fd.truncate() 
+0

用os.O_RDWR打开它在只读模式下打开它与我在python 2.6在linux中 – rutherford 2013-03-21 15:00:48

+1

我真的很激动,因为应用于文件使用单词“truncate”。截断一直意味着(对我来说)像“短片”。从“覆盖”来看,它的含义明显不同(对我来说)。 – 2013-05-02 23:25:56

+0

@StevenLu,我完全同意你的看法。无论如何,重要的是没有太多努力就可以获得理想的行为。这个代码与Ivo一样需要用上下文编写服务器锁。现在不记得了,但我发现不希望将上下文/信息存储在不同的文件中。也许我只是想模仿/var/run/foo.pid文件的行为,这些文件通常用作锁并存储当前运行的守护进程的pid。 – ceztko 2013-05-03 17:11:38

0

我不知道有任何优雅的方式来准确地做到这一点的红宝石。我的解决方案可能是创建一个临时文件,将内容写入它,然后将其重命名为我真正想要的文件名。如果它存在,这将覆盖以前的文件,或者如果它不存在,则创建该文件。事情是这样的:

orig_filename = './whatever_file.log' 
temp_filename = './.tempfile' 
temp_file = File.new(temp_filename, 'w') 

// Write contents to file 

temp_file.close 
File.rename(temp_filename, orig_filename) 

重命名将提高SystemCallError如果失败的话无论出于何种原因。

13

那么,只有这些模式,他们都有你列出的“缺陷”。

你唯一的选择是包装open()。 为什么不是这样的? (Python)

def touchopen(filename, *args, **kwargs): 
    open(filename, "a").close() # "touch" file 
    return open(filename, *args, **kwargs) 

它的行为就像打开,你甚至可以重新绑定它打开(),如果你真的想。

所有的开放式的特点被保留,你甚至可以这样做:

with touchopen("testfile", "r+") as testfile: 
    do_stuff() 

你当然可以创建它将会打开一个+模式下的文件contextmanager,读入内存,并拦截写得怎样对待截断通过在w模式下神奇地创建一个临时文件,并在关闭它时将该tempfile重命名为您的原始文件,但是我猜这样做会过度。

+1

+1,因为你添加了一些重要的线索。我想我找到了一个更好的答案(可能)受到较少的比赛条件的影响。它很快就会来临。 – ceztko 2012-04-27 13:58:25

+0

添加了我的答案。 – ceztko 2012-04-27 14:11:37

+0

啊,我明白了。我搜索了那样的东西,但没有找到它。很好的发现,如果这真的起作用,非常酷! – ch3ka 2012-04-27 14:20:46

2

你可以读,写,用“A +”(红宝石)截断:

File.open("test.txt", "a+") do |f| 
    f.print "abc\ndefgh" 
    f.rewind 
    p f.read 
    f.truncate(5) 
end 
puts File.size("test.txt") #=> 5 
+0

我使用Python,但行为似乎与红宝石相同。虽然'truncate(size)'工作的确如此,但不带参数的'truncate()'在append模式下(至少在linux中)不起作用。我的解决方案实现了我想要的确切行为。 – ceztko 2012-04-27 14:51:46

+0

在ruby中'truncate'必须有一个参数。 'f.truncate(f.pos)'会做一些事情,如“在这里截断!”。 – steenslag 2012-04-27 15:00:48

+0

嗯,真的:)另一个小陷阱是,在追加模式下,初始位置是文件的结尾,而不是开始。 – ceztko 2012-04-27 15:05:26

相关问题