Files

Link: https://rust.nizeclub.ru/_12.html#part01

Ввод-вывод построен вокруг модуля стандартной библиотеки std::io, который предоставляет инструменты для работы с потоками данных, а также модуля std::fs, предназначенного для операций с файловой системой.

Чтение файлов

Чтение текста из файлов в строку

Для чтения файлов, нужно сначала добавить несколько методов из библиотек: FIle - открытие файлов и Read - чтение.

use std::fs::File; // импорт File
use std::io::Read; // импорт Read

fn main() -> std::io::Result<()> { // отлов ошибок ввода/вывода
    let filename = "test.txt".to_string(); // путь до файла в корне проекта
    let mut filetext = String::new();
    let mut filehandle = File::open(filename)?; 
    filehandle.read_to_string(&mut filetext)?; // чтение в строку
    println!("{filetext}");
    Ok(())
}

Чтение в вектор из байтов (vector of bytes)

Чтение файла в память целиком как вектора байтов - для бинарных файлов, либо для частого обращения к содержимому:

use std::io::Read;
use std::{env, fs, io, str};

fn main() -> io::Result<()> {
    let mut file = fs::File::open("test_file.txt")?;
    let mut contents = Vec::new();
    file.read_to_end(&mut contents);
    println!("File contents: {:?}", contents); // вывод байт

    let text = match str::from_utf8(&contents) { // перевод в строку UTF8
        Ok(v) => v,
        Err(e) => panic!("Invalid UTF-8: {e}"),
    };
    println!("Result: {text}"); // вывод строкой
    Ok(())
}

 Запись в файлы

use std::fs::File;  
use std::io::Write;  
  
fn main() -> io::Result<()> {  
	// создать новый файл или обнулить имеющийся:
    let mut file = File::create("report.log")?;  
    file.write_all(b"report.log"); // запись байтов в файл
    Ok(())
}

Буферизация

Чтение текста через буфер

Нужно добавить к библиотеке File также библиотеку организации буфера чтения, а также обработать ошибки открытия файла и чтения.

use std::fs::File;
use std::io::{BufReader, Read};

fn main() -> std::io::Result<()> {
    let file = File::open("sportinv.csv")?;
    // обернуть File в буферизированный читатель (блоки по 8kb by default):
    let mut reader = BufReader::new(file); 
    let mut text = String::new();
    reader.read_to_string(&mut text)?;
    println!("{text}");
    Ok(())
}

Чтение текста из больших файлов в буфер по строчкам

use std::io::{BufRead, BufReader};
use std::fs::File;

fn main() -> io::Result<()> {
    let file = File::open("input.txt")?;
    let reader = BufReader::new(file);
    for line in reader.lines() { // io::BufRead::lines() итератор
        let line = line?;
        println!("{line}");
    }
    Ok(())

Запись в файл через буфер

use std::fs::File;
use std::io::{BufWriter, Write};

fn main() -> std::io::Result<()> {
    let file = File::create("output.txt")?;
    let mut writer = BufWriter::new(file);
    writer.write_all(b"Test output!")?;
    writer.flush()?; // Сброс буфера в файл
    Ok(())
}

Запись и дополнение файла через буфер по строчкам

use std::fs::File;
use std::io::{BufWriter, Write};

fn main() -> std::io::Result<()> {
    let file = File::create("output.txt")?;
    let mut writer = BufWriter::new(file);
    writeln!(writer, "Первая строка")?; // макрос записи в поток
    writeln!(writer, "Вторая строка")?;
    writer.flush()?;
    Ok(())
}

File::create всегда обнуляет файл, если он уже есть. Чтобы дописать файл, нужно использовать OpenOptions и указать режим append:

use std::fs::OpenOptions; // OpenOptions вместо File
use std::io::{BufWriter, Write};

fn main() -> std::io::Result<()> {
    let file = OpenOptions::new()
        .append(true) // +режим дозаписи
        .create(true) // создать, если файла нет
        .open("output.txt")?; // открыть файл
    //let file = File::create("output.txt")?;
    let mut writer = BufWriter::new(file);
    writeln!(writer, "Первая строка")?;
    writeln!(writer, "Вторая строка")?;
    writer.flush()?;
    Ok(())
}

Копирование файлов

Функция std::fs::copy принимает исходный и целевой пути и возвращает Result<u64, std::io::Error>, где u64 — количество скопированных байт.

use std::fs;
use std::path::Path;

fn main() {
    let sourcefile = "source.txt";
    let destfile = "dest.txt";
    if Path::new(sourcefile).exists() {
        match fs::copy(sourcefile, destfile) {
            Ok(bytes) => println!("Copied {bytes} bytes"),
            Err(e) => println!("Error: {e}"),
        }
    } else {
        println!("Исходного файла НЕТ!");
    }
}

Если целевой файл уже существует, он будет перезаписан.

Перемещение файлов

Функция std::fs::rename принимает исходный и целевой пути и возвращает Result<(), std::io::Error> (при успехе ничего не возвращает (()), при ошибке — описание проблемы):

use std::fs;

fn main() {
    match fs::rename("old.txt", "new.txt") {
        Ok(()) => println!("Move successful"),
        Err(e) => println!("Move failed {e}"),
    }
}
  • Если целевой файл существует, он будет перезаписан (на той же файловой системе);
  • Перемещение между разными файловыми системами может не сработать, надо сначала скопировать, а затем удалить исходный файл вручную.

Удаление файлов

Функция std::fs::remove_file принимает путь к файлу и возвращает результат типа Result<(), std::io::Error>, что позволяет обработать возможные ошибки (например, если файла не существует):

use std::fs;

fn main() {
    match fs::remove_file("sport.csv") {
        Ok(()) => println!("Remove successful"),
        Err(e) => println!("Remove failed {e}"),
    }
}

Аналогично с папками:

  • fs::remove_file — для файлов.
  • fs::remove_dir — для пустых директорий.
  • fs::remove_dir_all — для директорий с содержимым.

Пути в файловой системе

Пути в Rust обрабатываются через структуры Path и PathBuf, где Path — это неизменяемый срез пути, аналогичный str:

use std::path::Path;

fn main() {
    let path = Path::new("/home/alex/example.txt"); // ссылка на путь
    println!("Extension: {:?}", path.extension()); // Some("txt")
    println!("File name: {:?}", path.file_name()); // Some("example.txt")
    println!("File exists? {}", path.exists()); // false
}

Методы вроде extension() и file_name() возвращают Option, т.к. путь может не содержать этих элементов.

PathBuf — это изменяемая версия пути, аналогичная String:

use std::path::PathBuf;

fn main() {
    let mut path = PathBuf::from("/home/alex/");
    // push добавляет компонент к пути с учетом разделителей ОС:
    path.push("test.txt"); 
    println!("Path: {:?}", path); // /home/alex/test.txt
}

Используйте Path для проверки существующих путей, а PathBuf — для построения новых.

Получить список файлов

Функция read_dir:

use std::fs;
fn main() {
    let paths = fs::read_dir("./").unwrap();

    for path in paths {
        println!("{}", path.unwrap().path().display())
    }
}

Системные команды

Основной инструмент для запуска внешних программ — это модуль std::process, в частности структуры Command и Output.

Получить список файлов с их абсолютным путём в системе

use std::fs;
use std::process;

fn main() {
    let paths = fs::read_dir("./").unwrap();

    for path in paths {
        let pathbuf = path.unwrap().path();
        println!("{:?}", fs::canonicalize(&pathbuf));
    }

    println!("\nPID = {}\n", process::id());
}