Каков де-факто способ чтения и записи файлов в Rust 1.x?


С ржавчиной будучи сравнительно новым, я видел слишком много способов чтения и записи файлов. Многие из них являются чрезвычайно грязными фрагментами, которые кто-то придумал для своего блога, и 99% примеров, которые я нашел (даже при переполнении стека), являются нестабильными сборками, которые больше не работают. Теперь, когда Rust стабильна, что такое простой, читаемый, не паникующий фрагмент для чтения или записи файлов?

Это самое близкое, что я получил к чему-то, что работает с точки зрения чтения текстового файла, но он все еще не компилируется, хотя я довольно уверен, что включил все, что должен был иметь. Это основано на фрагменте, который я нашел в Google+ из всех мест, и единственное, что я изменил, это то, что старый BufferedReader сейчас BufReader:

use std::fs::File;
use std::io::BufReader;
use std::path::Path;

fn main() {
    let path = Path::new("./textfile");
    let mut file = BufReader::new(File::open(&path));
    for line in file.lines() {
        println!("{}", line);
    }
}

компилятор жалуется:

error: the trait bound `std::result::Result<std::fs::File, std::io::Error>: std::io::Read` is not satisfied [--explain E0277]
 --> src/main.rs:7:20
  |>
7 |>     let mut file = BufReader::new(File::open(&path));
  |>                    ^^^^^^^^^^^^^^
note: required by `std::io::BufReader::new`

error: no method named `lines` found for type `std::io::BufReader<std::result::Result<std::fs::File, std::io::Error>>` in the current scope
 --> src/main.rs:8:22
  |>
8 |>     for line in file.lines() {
  |>                      ^^^^^

подводя итог, то, что я ищу, это:

  • краткость
  • читабельности
  • охватывает все возможные ошибки
  • не паника
1   59   2015-07-02 22:23:46

1 ответ:

ни одна из функций, которые я показываю здесь, не паникует сама по себе, но я использую expect потому что я не знаю, какая обработка ошибок будет лучше всего вписываться в ваше приложение. Иди читай Язык Программирования Ржавчины ' s глава об обработке ошибок чтобы понять, как правильно обрабатывать отказ в вашей собственной программе.

ржавчина 1.26 и вперед

если вы не хотите заботиться о базовых деталях, есть однострочные функции для чтение и письмо.

читать файл String

use std::fs;

fn main() {
    let data = fs::read_to_string("/etc/hosts").expect("Unable to read file");
    println!("{}", data);
}

читать файл как Vec<u8>

use std::fs;

fn main() {
    let data = fs::read("/etc/hosts").expect("Unable to read file");
    println!("{}", data.len());
}

записать файл

use std::fs;

fn main() {
    let data = "Some data!";
    fs::write("/tmp/foo", data).expect("Unable to write file");
}

Rust 1.0 и далее

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

чтение данные

чтение файла требует двух основных частей: File и Read.

читать файл String

use std::fs::File;
use std::io::Read;

fn main() {
    let mut data = String::new();
    let mut f = File::open("/etc/hosts").expect("Unable to open file");
    f.read_to_string(&mut data).expect("Unable to read string");
    println!("{}", data);
}

читать файл как Vec<u8>

use std::fs::File;
use std::io::Read;

fn main() {
    let mut data = Vec::new();
    let mut f = File::open("/etc/hosts").expect("Unable to open file");
    f.read_to_end(&mut data).expect("Unable to read data");
    println!("{}", data.len());
}

записать файл

написание файла аналогично, за исключением того, что мы используем Write черта и мы всегда записывать байт. Вы можете конвертировать String/&str в байт с as_bytes:

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

fn main() {
    let data = "Some data!";
    let mut f = File::create("/tmp/foo").expect("Unable to create file");
    f.write_all(data.as_bytes()).expect("Unable to write data");
}

Буферизованный Ввод-Вывод

я почувствовал небольшой толчок от сообщества, чтобы использовать BufReader и BufWriter вместо чтения прямо из файла

буферизованный читатель (или писатель) использует буфер для уменьшения количества запросов ввода-вывода. Например, гораздо эффективнее получить доступ к диску один раз, чтобы прочитать 256 байт вместо доступа к диску 256 раз.

это, как говорится, я не верю буферизованное устройство чтения / записи будет полезно при чтении всего файла. read_to_end похоже, что данные копируются в несколько больших кусках, поэтому передача уже может быть естественным образом объединена в меньшее количество запросов ввода-вывода.

вот пример использования его для чтения:

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

fn main() {
    let mut data = String::new();
    let f = File::open("/etc/hosts").expect("Unable to open file");
    let mut br = BufReader::new(f);
    br.read_to_string(&mut data).expect("Unable to read string");
    println!("{}", data);
}

и пишем:

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

fn main() {
    let data = "Some data!";
    let f = File::create("/tmp/foo").expect("Unable to create file");
    let mut f = BufWriter::new(f);
    f.write_all(data.as_bytes()).expect("Unable to write data");
}

A BufReader это более полезно, когда вы хотите читать построчно:

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

fn main() {
    let f = File::open("/etc/hosts").expect("Unable to open file");
    let f = BufReader::new(f);

    for line in f.lines() {
        let line = line.expect("Unable to read line");
        println!("Line: {}", line);
    }
}