我试图在文本文件的开头添加一个新行。我首先使用append
打开文件,但这只允许我使用write_all
写入文件的末尾,至少是我得到的结果。如果我正确阅读文档,这是设计的。
我试图与seek
一起玩,但这并没有解决它。
这就是我目前的情况:
let mut file = OpenOptions::new().append(true).open(&file_path).unwrap();
file.seek(SeekFrom::Start(0));
file.write_all(b"Cool days\n");
如果我用write
打开文件,我最终会覆盖数据而不是添加。用Rust实现这个目的的合适方法是什么?
答案 0 :(得分:11)
您无法直接在任何编程语言中执行此操作。在C#,Python,NodeJs,PHP,Bash和C中查看有关同一主题的其他一些问题。
有几种解决方案有不同的权衡:
将整个文件复制到内存中,写入您想要的数据,然后在其后写入文件的其余部分。如果文件很大,这可能是一个糟糕的解决方案,因为它将使用大量内存,但可能适合小文件,因为它很容易实现。
使用与您要添加的文本大小相同的缓冲区。将文件的块一次复制到内存中,然后用前一个块覆盖它。通过这种方式,您可以随时使用新文本随机播放文件的内容。这可能比其他方法慢,但不需要大量内存分配。当进程没有删除文件的权限时,它也可能是最佳选择。但要小心:如果进程中断,这种方法可能会使文件处于损坏状态。
将新数据写入临时文件,然后附加原始内容。然后删除原始文件并重命名临时文件。这是一个很好的解决方案,因为它将繁重的工作委托给操作系统,并且原始数据被备份,因此如果进程中断则不会损坏。
从Stack Overflow上搜索,第三种解决方案似乎是其他语言最受欢迎的答案,例如: in Bash。这很可能是因为它快速,安全,通常只需几行代码即可实现。
快速的Rust版本看起来像这样:
extern crate mktemp;
use mktemp::Temp;
use std::{fs, io, io::Write, fs::File, path::Path};
fn prepend_file<P: AsRef<Path>>(data: &[u8], file_path: &P) -> io::Result<()> {
// Create a temporary file
let mut tmp_path = Temp::new_file()?;
// Stop the temp file being automatically deleted when the variable
// is dropped, by releasing it.
tmp_path.release();
// Open temp file for writing
let mut tmp = File::create(&tmp_path)?;
// Open source file for reading
let mut src = File::open(&file_path)?;
// Write the data to prepend
tmp.write_all(&data)?;
// Copy the rest of the source file
io::copy(&mut src, &mut tmp)?;
fs::remove_file(&file_path)?;
fs::rename(&tmp_path, &file_path)?;
Ok(())
}
用法:
fn main() -> io::Result<()> {
let file_path = Path::new("file.txt");
let data = "Data to add to the beginning of the file\n";
prepend_file(data.as_bytes(), &file_path)?;
Ok(())
}