【问题标题】:How to open (read-write) or create a file with truncation allowed?如何打开(读写)或创建允许截断的文件?
【发布时间】:2012-04-27 11:27:13
【问题描述】:

我想:

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

编辑:截断我的意思是写到一个位置并丢弃文件的剩余部分(如果存在)

所有这些都是原子的(使用单个 open() 调用或模拟单个 open() 调用)

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

  • r : 显然不行;
  • r+ : 如果文件不存在则失败;
  • w:如果文件存在则重新创建;
  • w+:如果文件存在则重新创建;
  • a:无法阅读;
  • a+:不能截断。

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

来自 Ruby 的一些 doc(也适用于 python):

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.

【问题讨论】:

  • 所以您实际上是想覆盖一个文件并确保之前的内容不会停留在您停止写入的点以下?
  • 是的。 file.truncate() 用于此目的,并且在文件为“r+”、“w”、“w+”时有效。但它们都有我上面列出的缺陷。

标签: python ruby truncate


【解决方案1】:

根据OpenGroup

O_TRUNC

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

所以,当使用“w”或“w+”打开文件时,可能会传递 O_TRUNC。这赋予了“截断”不同的含义,而不是我想要的。

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

以下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()

【讨论】:

  • 用 os.O_RDWR 打开它,我在 linux 中的 python 2.6 上以只读模式打开它
  • 我对文件中使用“截断”一词感到非常不安。截断一直意味着(对我来说)“剪辑短”之类的东西。它与“覆盖”的含义(再次对我而言)明显不同。
  • @StevenLu,我完全同意你的看法。无论如何,重要的是无需太多努力即可获得所需的行为。与 Ivo 一样,需要此代码来编写带有上下文的服务器锁。现在不记得了,但我发现将上下文/信息存储在不同的文件中是不可取的。也许我只是想模仿 /var/run/foo.pid 文件的行为,这些文件通常用作锁并存储当前运行的守护进程的 pid。
【解决方案2】:

嗯,只有这些模式,而且都有你列出的“缺陷”。

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

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

它的行为就像 open,如果你真的愿意,你甚至可以将它重新绑定到 open()。

所有 open 的功能都被保留了,你甚至可以这样做:

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

您当然可以创建一个上下文管理器,它以 a+ 模式打开文件,将其读入内存并拦截写入,因此您可以通过在 w 模式下神奇地创建一个临时文件来处理截断,并在您使用时将该临时文件重命名为原始文件关闭它,但我猜那会是矫枉过正。

【讨论】:

  • +1 因为你添加了一些重要的线索。我想我找到了一个更好的答案,即(可能)受到较少竞争条件的影响。它很快就会到来。
  • 啊,我明白了。我搜索了类似的东西,但没有找到。很好的发现,如果这真的有效,那就太酷了!
【解决方案3】:

您可以使用“a+”(Ruby)进行读取、写入和截断:

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

【讨论】:

  • 我正在使用 python,但与 ruby​​ 相比,行为似乎相同。虽然 truncate(size) 确实有效,但不带参数的 truncate() 在附加模式下(至少在 linux 中)的工作方式不同。我的解决方案实现了我想要的确切行为。
  • 在 ruby​​ 中 truncate 必须有一个参数。 f.truncate(f.pos) 会做一些“在这里截断!”。
  • 嗯,没错 :) 另一个小陷阱是,在追加模式下,初始位置是文件的结尾,而不是开头。
【解决方案4】:

我不知道在 Ruby 中有什么优雅的方法可以做到这一点。我的解决方案可能是创建一个临时文件,向其中写入内容,然后将其重命名为我真正想要的文件名。如果存在,这将覆盖先前的文件,如果不存在,则创建该文件。像这样的:

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

【讨论】:

    猜你喜欢
    • 2015-10-07
    • 2021-01-31
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-14
    • 1970-01-01
    相关资源
    最近更新 更多