Rust 1.xでファイルを読み書きする実際の方法は何ですか?


136

Rustは比較的新しいため、ファイルを読み書きする方法が多すぎます。多くは、誰かがブログのために思いついた非常に厄介なスニペットであり、私が見つけた例の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つの文字列に入れますか?「ファイルを読み取る」には、複数の方法があります。
Shepmaster 2015

どちらの方法でもかまいません。意図的に開いたままにしました。すべてが1つの文字列に収集されている場合、Vec <String>に分割するのは簡単です。逆も同様です。ソリューションの検索のこの時点で、エレガントで最新のRustファイルI / Oコードが機能することを確認できてうれしいです。
Jared、2015

3
特性エラー(std::io::Read)については、Rustでは明示的に使用することが予想される特性をインポートする必要があることに注意してください。したがって、ここでは欠落しているuse std::io::Readuse std::io::{Read,BufReader}2つの用途を結合するためのa になる可能性があります)
Matthieu M.

回答:


197

ここで示す関数自体はパニックexpectになりませんが、どのエラー処理がアプリケーションに最適かわからないため、使用しています。エラー処理に関するRustプログラミング言語章を読んで、自分のプログラムの失敗を適切に処理する方法を理解してください。

Rust 1.26以降

基本的な詳細を気にしたくない場合は、読み取りと書き込み用の1行の関数があります。

ファイルを読み込む 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を割り当てる1行の関数よりも少し冗長ですが、割り当てられたデータを再利用したり、既存のオブジェクトに追加したりできるという点でより強力です。

データの読み取り

ファイルを読み取るには、2つのコアピースが必要です:FileRead

ファイルを読み込む 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

私が使用するコミュニティーからのプッシュのビットを感じましたBufReaderし、BufWriter代わりにファイルから直接読み取ります

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

とはいえ、ファイル全体を読み取るときにバッファー付きリーダー/ライターが役立つとは思いません。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);
    }
}

2
私はこれに基づいて本当に多くを持っているわけではありませんが、これを調査している間、私はコミュニティから、ファイルから文字列に直接読み取るのではなく、BufReaderとBufWriterを使用するという少しのプッシュを感じました。これらのオブジェクトについて、または回答で示した「よりクラシックな」バージョンに対してそれらを使用することの長所と短所について、よく知っていますか?
Jared

@TheDaleks私はあなたの質問をフォローしていません。b"foobar"バイト配列(&[u8; N])への参照を作成するためのリテラルです。そのため、それは不変です。単純な方法ではできないことは何もありません。
シェプマスター

@Shepmasterエンコードされた文字列の代わりにバイト配列を使用すると便利な場合があります。たとえば、ファイルをある場所から別の場所に移動するアプリを作成する場合、アプリが処理する実行可能ファイルを破損しないように、生のバイト数が必要です。
The Daleks

@TheDaleksはい、これが、この回答Vec<u8>が読み書きにを使用する方法を説明する理由です。それらは生のバイトです。
シェプマスター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.