如何在写入文件之前删除文件内容?

5
我写了一个机器人,定期执行一些网络查询,并将最新状态转储到存储在磁盘上的文件中。这是我的代码:
let log_file = OpenOptions::new()
    .read(true)
    .write(true)
    .create(true)
    .open(&log_file_name)
    .unwrap();
serde_json::to_writer(
    log_file,
    &State {
        foo: self.foo,
        bar: self.bar
    },
)
.unwrap();

问题在于,新状态JSON的大小偶尔会比先前状态JSON的大小小。这将使得先前状态JSON中长度大于新状态JSON的部分仍然留在日志文件中:
{"foo": 1,"bar": 2}"baz": 3}

在上面的示例中,"baz": 3} 是先前状态JSON的剩余数据。
问题是:我如何重写代码,以便首先刷新文件的所有内容,然后再写入新的状态JSON?
我知道删除文件是一种解决方案,但我不想这样做。

有没有具体的原因,解释为什么删除文件不可取?状态文件的常见问题是需要原子性地替换它们。它们需要包含旧内容或新内容,永远不会有中间状态。使用 truncate 的解决方案可能会留下一个空文件,例如。这是个问题吗? - Jonathan Giddy
主要原因是我对如何截断文件内容感到好奇。但你说得很对:我应该重构代码以删除文件,因为那样会提供更好的保证。 - Paul Razvan Berg
2个回答

12

你还需要使用truncate标志,如果文件已经存在,则将其截断为长度为0。此外,由于你实际上不是从日志文件读取内容,因此应删除read 标志。

由于writecreatetruncate是常见的标志组合,因此有一个快捷方式:您不需要使用更低级别的OpenOptions接口,而可以使用File::create()

let log_file = File::create(&log_file_name).unwrap();

File::create() 的实现只是使用 OpenOptions 明确拼写了长版本:

pub fn create<P: AsRef<Path>>(path: P) -> io::Result<File> {
    OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
}

4

使用.truncate()。例如:

let log_file = OpenOptions::new()
    .read(true)
    .write(true)
    .create(true)
    .truncate(true) // added here
    .open(&log_file_name)
    .unwrap();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接