Read & Write from Files

Read Lines of Strings from a File

std cat-filesystem

Writes a three-line message to a file, then reads it back a line at a time with the std::io::Lines⮳ iterator created by std::io::BufRead::linesstd::fs::File⮳ implements std::io::Read⮳ which provides std::io::BufReader⮳ trait. std::fs::File::create⮳ opens a std::fs::File⮳ for writing, std::fs::File::open⮳ for reading.

//! Demonstrates writing to and reading from a file.

use std::fs;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Error;
use std::io::Write;

fn main() -> Result<(), Error> {
    let path = "temp/lines.txt";

    // Create a file if it does not exist, and will truncate it if it does.
    let mut output = File::create(path)?;
    // Write to the file.
    write!(&mut output, "Rust\n💖\nFun")?;

    // Open the file for reading.
    let input = File::open(path)?;
    // Adds buffering.
    let buffered = BufReader::new(input);

    // Iterate over the lines of this reader.
    for line in buffered.lines() {
        println!("{}", line?);
    }

    Ok(())
}

Avoid Writing and Reading from the Same File

same-file cat-filesystem

Use same_file::Handle⮳ to a file that can be tested for equality with other handles. In this example, the handles of file to be read from and to be written to are tested for equality.

//! This example demonstrates how to check whether two file paths refer to the
//! same file or directory.

use std::fs;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Error;
use std::io::ErrorKind;
use std::path::Path;

use same_file::Handle;

/// Check whether we are reading & writing to the same file.
fn main() -> Result<(), Error> {
    // Get a Handle for the standard output.
    let stdout_handle = Handle::stdout()?;

    // Get a Handle for the file we want to read.
    let path_to_read = Path::new("temp/new.txt");
    let handle = Handle::from_path(path_to_read)?;

    // Check if the file handle is the same as the standard output handle.
    if stdout_handle == handle {
        return Err(Error::new(
            ErrorKind::Other,
            "You are reading and writing to the same file!",
        ));
    } else {
        // Print the contents of the file.
        let file = File::open(path_to_read)?;
        let file = BufReader::new(file);
        for (num, line) in file.lines().enumerate() {
            println!("{} : {}", num, line?.to_uppercase());
        }
    }

    Ok(())
}
cargo run

displays the contents of the file new.txt.

cargo run >> new.txt

errors because the two files are same.

Access a File Randomly Using a Memory Map

memmap2 cat-filesystem

Creates a memory map of a file using memmap2⮳ and simulates some non-sequential reads from the file. Using a memory map means you just index into a slice rather than dealing with std::fs::File::seek⮳ to navigate a std::fs::File⮳.

The memmap2::Mmap::map⮳ function assumes the file behind the memory map is not being modified at the same time by another process or else a race condition⮳ occurs.

//! Demonstrates reading from a memory-mapped file.

use std::fs;
use std::fs::File;
use std::io::Error;
use std::io::Write;

use memmap2::Mmap;

fn main() -> Result<(), Error> {
    write!(
        File::create("temp/content.txt")?,
        "My hovercraft is full of eels!"
    )?;
    // Open the file for reading.
    let file = File::open("temp/content.txt")?;
    // Create a memory map of the file. This is unsafe because it relies on the
    // file not being modified externally.
    let map = unsafe { Mmap::map(&file)? };
    // Print the memory map (for debugging purposes).
    println!("{:?}", map);
    // Define some random indexes into the memory map.
    let random_indexes = [0, 1, 2, 19, 22, 10, 11, 29];
    // Assert that a slice of the memory map matches the expected bytes.
    assert_eq!(&map[3..13], b"hovercraft");
    // Collect bytes from the memory map at the random indexes.
    let random_bytes: Vec<u8> =
        random_indexes.iter().map(|&idx| map[idx]).collect();
    assert_eq!(&random_bytes[..], b"My loaf!");
    Ok(())
}