1. ホーム
  2. file-io

[解決済み】Rust 1.xでファイルを読み書きするデファクトの方法は?

2022-04-10 01:54:47

質問

Rustは比較的新しいので、ファイルの読み書きの方法があまりにも多く見受けられます。その多くは、誰かが自分のブログのために思いついた非常に雑なスニペットで、私が見つけた例の99%は(Stack Overflowでさえ)、もはや動作しない不安定なビルドのものです。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() {
  |>                      ^^^^^

要約すると、私が求めているのは

  • 簡潔さ
  • 可読性
  • 起こりうるすべてのエラーをカバー
  • パニックにならない

解決方法は?

ここで紹介する関数はどれも単体ではパニックになりませんが、私は expect というのも、どのようなエラーハンドリングがあなたのアプリケーションに最も適しているかわからないからです。読みに行く Rust プログラミング言語 's エラー処理に関する章 を読んで、自分のプログラムでの失敗を適切に処理する方法を理解してください。

Rust 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 以降

これらの書式は、1行の関数で String または Vec が、割り当てられたデータを再利用したり、既存のオブジェクトに追加したりできる点で、より強力です。

データの読み込み

ファイルを読むには、2つの核となる部分が必要です。 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");
}

バッファードI/O

<ブロッククオート

というコミュニティからの後押しを少し感じました。 BufReaderBufWriter ファイルから直接読み込むのではなく

バッファードリーダ(またはライタ)は、バッファを使用してI/O要求の回数を減らします。例えば、256バイトを読み込むためにディスクに256回アクセスするよりも、1回アクセスする方がはるかに効率的です。

とはいえ、ファイル全体を読むときにバッファード・リーダ/ライタが役に立つとは思えません。 read_to_end は、ある程度大きなチャンクでデータをコピーしているようなので、転送はすでに自然に少ないI/Oリクエストにまとまっているかもしれません。

ここでは、読書に使用した例をご紹介します。

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);
    }
}