Эффективное чтение файлов на Rust

RustRustBeginner
Практиковаться сейчас

This tutorial is from open-source community. Access the source code

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом лабораторном задании мы получаем наивную реализацию и более эффективную реализацию для чтения строк из файла на Rust. Наивный подход использует read_to_string для чтения файла в одну строку, а затем разделяет ее на строки, в то время как более эффективный подход использует BufReader для чтения файла построчно, не загружая все содержимое в память.

Примечание: Если в лабораторном задании не указано имя файла, вы можете использовать любое имя файла, которое хотите. Например, вы можете использовать main.rs, скомпилировать и запустить его с помощью rustc main.rs &&./main.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("Rust")) -.-> rust/BasicConceptsGroup(["Basic Concepts"]) rust(("Rust")) -.-> rust/ControlStructuresGroup(["Control Structures"]) rust(("Rust")) -.-> rust/FunctionsandClosuresGroup(["Functions and Closures"]) rust(("Rust")) -.-> rust/DataStructuresandEnumsGroup(["Data Structures and Enums"]) rust(("Rust")) -.-> rust/AdvancedTopicsGroup(["Advanced Topics"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("Variable Declarations") rust/BasicConceptsGroup -.-> rust/mutable_variables("Mutable Variables") rust/ControlStructuresGroup -.-> rust/for_loop("for Loop") rust/FunctionsandClosuresGroup -.-> rust/function_syntax("Function Syntax") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("Expressions and Statements") rust/DataStructuresandEnumsGroup -.-> rust/method_syntax("Method Syntax") rust/AdvancedTopicsGroup -.-> rust/operator_overloading("Traits for Operator Overloading") subgraph Lab Skills rust/variable_declarations -.-> lab-99272{{"Эффективное чтение файлов на Rust"}} rust/mutable_variables -.-> lab-99272{{"Эффективное чтение файлов на Rust"}} rust/for_loop -.-> lab-99272{{"Эффективное чтение файлов на Rust"}} rust/function_syntax -.-> lab-99272{{"Эффективное чтение файлов на Rust"}} rust/expressions_statements -.-> lab-99272{{"Эффективное чтение файлов на Rust"}} rust/method_syntax -.-> lab-99272{{"Эффективное чтение файлов на Rust"}} rust/operator_overloading -.-> lab-99272{{"Эффективное чтение файлов на Rust"}} end

read_lines

Наивный подход

Это может быть разумная первая попытка для начинающего программиста при первом реализации чтения строк из файла.

use std::fs::read_to_string;

fn read_lines(filename: &str) -> Vec<String> {
    let mut result = Vec::new();

    for line in read_to_string(filename).unwrap().lines() {
        result.push(line.to_string())
    }

    result
}

Поскольку метод lines() возвращает итератор по строкам в файле, мы также можем выполнить map inline и собрать результаты, получая более краткое и выразительное выражение.

use std::fs::read_to_string;

fn read_lines(filename: &str) -> Vec<String> {
    read_to_string(filename)
     .unwrap()  // panic при возможных ошибках чтения файла
     .lines()  // разделить строку на итератор строковых срезов
     .map(String::from)  // превратить каждый срез в строку
     .collect()  // собрать их в вектор
}

Обратите внимание, что в обоих примерах выше мы должны преобразовать &str ссылку, возвращаемую из lines(), в собственный тип String, используя .to_string() и String::from соответственно.

Более эффективный подход

Здесь мы передаем владение открытым File в структуру BufReader. BufReader использует внутренний буфер для уменьшения промежуточных выделений памяти.

Мы также обновляем read_lines для возврата итератора вместо выделения новых объектов String в памяти для каждой строки.

use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;

fn main() {
    // Файл hosts.txt должен существовать в текущем каталоге
    if let Ok(lines) = read_lines("./hosts.txt") {
        // Консумирует итератор, возвращает (Optional) String
        for line in lines {
            if let Ok(ip) = line {
                println!("{}", ip);
            }
        }
    }
}

// Результат обернут в Result для обработки ошибок
// Возвращает итератор для Reader строк файла.
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where P: AsRef<Path>, {
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}

Запуск этой программы просто выводит строки по отдельности.

$ echo -e "127.0.0.1\n192.168.0.1\n" > hosts.txt
$ rustc read_lines.rs && ./read_lines
127.0.0.1
192.168.0.1

(Обратите внимание, что поскольку File::open ожидает обобщенный AsRef<Path> в качестве аргумента, мы определяем наш обобщенный метод read_lines() с тем же обобщенным ограничением, используя ключевое слово where.)

Этот процесс более эффективен, чем создание String в памяти с содержимым всего файла. Это особенно может вызывать проблемы с производительностью при работе с большими файлами.

Резюме

Поздравляем! Вы завершили лабораторную работу Read_lines. Вы можете практиковаться в других лабораторных работах в LabEx, чтобы улучшить свои навыки.