将行添加到文件开头

时间:2017-04-16 19:23:47

标签: file io rust

我试图在文本文件的开头添加一个新行。我首先使用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实现这个目的的合适方法是什么?

1 个答案:

答案 0 :(得分:11)

您无法直接在任何编程语言中执行此操作。在C#PythonNodeJsPHPBashC中查看有关同一主题的其他一些问题。

有几种解决方案有不同的权衡:

  1. 将整个文件复制到内存中,写入您想要的数据,然后在其后写入文件的其余部分。如果文件很大,这可能是一个糟糕的解决方案,因为它将使用大量内存,但可能适合小文件,因为它很容易实现。

  2. 使用与您要添加的文本大小相同的缓冲区。将文件的块一次复制到内存中,然后用前一个块覆盖它。通过这种方式,您可以随时使用新文本随机播放文件的内容。这可能比其他方法慢,但不需要大量内存分配。当进程没有删除文件的权限时,它也可能是最佳选择。但要小心:如果进程中断,这种方法可能会使文件处于损坏状态。

  3. 将新数据写入临时文件,然后附加原始内容。然后删除原始文件并重命名临时文件。这是一个很好的解决方案,因为它将繁重的工作委托给操作系统,并且原始数据被备份,因此如果进程中断则不会损坏。

  4. 从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(())
    }