I’m attempting to add a new line to the beginning of a textual content file. I begin by opening the file with append
however that solely permits me to use write_all
to write to the finish of the file, not less than that’s the end result I’m getting. If I learn the documentation appropriately, that is by design.
I’ve tried to play with search
, however that didn’t clear up it.
This is what I have at the moment:
let mut file = OpenChoices::new().append(true).open(&file_path).unwrap();
file.search(SeeokayFrom::Start(0));
file.write_all(b"Cool daysn");
If I open the file with write
, I find yourself overriding information as an alternative of including. What could be the applicable method to go about carrying out this with Rust?
You can’t do that straight in any programming language. See another questions on the similar matter in C#, Python, NodeJs, PHP, Bash and C.
There are a number of options with completely different trade-offs:
-
Copy the whole file into reminiscence, write the information you need, after which write the relaxation of the file after it. If the file is massive, this may be a unhealthy resolution as a result of of the quantity of reminiscence it would use, however may be appropriate for small information, as a result of it’s easy to implement.
-
Use a buffer, the similar measurement as the textual content you need to prepend. Copy chunks of the file at a time into reminiscence after which overwrite it with the earlier chunk. In this fashion, you can shuffle the contents of the file alongside, with the new textual content at the begin. This is probably going to be slower than the different approaches, however is not going to require a massive reminiscence allocation. It may be the best option when the course of doesn’t have permission to delete the file. But watch out: If the course of is interrupted, this method might go away the file in a corrupted state.
-
Write the new information to a momentary file after which append the contents of the unique. Then delete the unique and rename the momentary file. This is a good resolution as a result of it delegates the heavy lifting to the working system, and the unique information is backed up so is not going to be corrupted if the course of is interrupted.
From looking on Stack Overflow, the third resolution appears to be the hottest reply for different languages, e.g. in Bash. This is probably going to be as a result of it’s quick, protected and can usually be carried out in simply a few traces of code.
A fast Rust model appears to be like one thing like this:
extern crate mktemp;
use mktemp::Temp;
use std::{fs, io, io::Write, fs::File, path::Path};
fn prepend_file<P: AsRef<Path>>(information: &u8, file_path: &P) -> io::Result<()> {
// Create a momentary file
let mut tmp_path = Temp::new_file()?;
// Stop the temp file being mechanically deleted when the variable
// is dropped, by releasing it.
tmp_path.launch();
// Open temp file for writing
let mut tmp = File::create(&tmp_path)?;
// Open supply file for studying
let mut src = File::open(&file_path)?;
// Write the information to prepend
tmp.write_all(&information)?;
// Copy the relaxation of the supply file
io::copy(&mut src, &mut tmp)?;
fs::remove_file(&file_path)?;
fs::rename(&tmp_path, &file_path)?;
Ok(())
}
Usage:
fn principal() -> io::Result<()> {
let file_path = Path::new("file.txt");
let information = "Data to add to the beginning of the filen";
prepend_file(information.as_bytes(), &file_path)?;
Ok(())
}
An instance with 4 steps: (Peter’s resolution #1)
- create a vec holding information to preprend
- open file, learn content material and append to vec
- create file with similar path, this may truncate (see doc File::create)
- write content material
use std::fs::File;
use std::path::Path;
use std::io::{Read, Write, Result};
fn prepend_file<P: AsRef<Path> + ?Sized>(information: &u8, path: &P) -> Result<()> {
let mut f = File::open(path)?;
let mut content material = information.to_owned();
f.read_to_end(&mut content material)?;
let mut f = File::create(path)?;
f.write_all(content material.as_slice())?;
Ok(())
}
fn principal() -> Result<()> {
prepend_file("hello worldn".as_bytes(), "/tmp/file.txt")
}
Peter’s resolution works good, nevertheless it brings an additional crate, when coping with small information, it’s good to keep away from it.