Введение
В этом лабораторном задании мы получаем наивную реализацию и более эффективную реализацию для чтения строк из файла на Rust. Наивный подход использует read_to_string для чтения файла в одну строку, а затем разделяет ее на строки, в то время как более эффективный подход использует BufReader для чтения файла построчно, не загружая все содержимое в память.
Примечание: Если в лабораторном задании не указано имя файла, вы можете использовать любое имя файла, которое хотите. Например, вы можете использовать
main.rs, скомпилировать и запустить его с помощьюrustc main.rs &&./main.
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, чтобы улучшить свои навыки.