Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to replace a word in a file in a .txt

Tags:

rust

I want to open a file and replace just one word in it. I can't seem to find any method of doing that except by creating a new file with the new text.

like image 216
user3473152 Avatar asked Nov 30 '14 16:11

user3473152


People also ask

How do I replace data in a text file?

1) Copy all the text up to the start of the line before which you want to insert the new line from the old file to the new file. 2) Write your new text to the new file, with a newline character at the end. 3) Copy all the text up to the end of the file from the old file to the new file. 4) Close both files.


1 Answers

A file is a sequence of bytes. For performance reasons, a file almost always occupies one chunk of space on the hard drive. It is also easier to manipulate a contiguous file from programmer's point of view, because in this case a file can be thought of as a plain array; otherwise there should be some linked-list-like or tree-like data structure, which in 99% of cases would only make programming harder. Consequently, a file can easily be appended to, but inserting or deleting data in the middle is harder. This is usually done in five steps (for insertion; removal is very similar):

  1. create a new temporary file (or, if a file is of moderate size, create a buffer in memory)
  2. copy everything before the insertion point to this new file
  3. write the new data in this new file or
  4. copy everything after the insertion point to the new file
  5. after everything is copied, the temporary file is moved to replace the original one

Replacing words of possibly different length in general involves either removal or insertion. For files of decent size the simplest way is to read the whole source file into memory, run a replace operation on it and dump the results back into the original file. This way items 2-4 will be done automatically for you by the library code for string operations. Here is an example program (it takes source word, replacement word and file path from command line arguments):

use std::env;
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::Path;

fn main() {
    // Handle errors
    run().unwrap();
}

fn run() -> Result<(), io::Error> {
    // Extract words and file path from the command line args
    let args: Vec<String> = env::args().skip(1).collect();
    if args.len() != 3 {
        println!("Wrong number of arguments");
        return Ok(());
    }

    let word_from = &args[0];
    // If the source word is empty then there is nothing to replace
    if word_from.is_empty() { return Ok(()); }  

    let word_to = &args[1];

    let file_name = &args[2];
    let file_path = Path::new(&file_name);

    // Open and read the file entirely
    let mut src = File::open(&file_path)?;
    let mut data = String::new();
    src.read_to_string(&mut data)?;
    drop(src);  // Close the file early

    // Run the replace operation in memory
    let new_data = data.replace(&*word_from, &*word_to);

    // Recreate the file and dump the processed contents to it
    let mut dst = File::create(&file_path)?;
    dst.write(new_data.as_bytes())?;

    println!("done");

    Ok(())
}

Note that creating a temporary file is still a good idea because writing a large chunk of data to a file is not an atomic operation, while renaming a file usually is. Hence if something goes wrong and you don't use a temporary file, your source file will likely be corrupted. If you do use a temporary file, then the source file will either be replaced entirely or not.

If your files are large (that is, several gigabytes and larger), streaming replace is probably a good idea. In this case you will need to read your file in chunks (down to 1 byte length, which may make it easier) and run the replace operation in these chunks, writing the result to the temporary file. After the whole source file is processed, the temporary file is moved over it. If you read in chunks larger than single byte, you will also need to handle the situation when your word is "split" between these chunks.

like image 143
Vladimir Matveev Avatar answered Sep 28 '22 05:09

Vladimir Matveev