(DAY23) 俺と学ぶRust言語~ファイルI/O~

# 今日の内容
- ファイル読み込み
- ファイル書き込み

# はじめに
[前回](https://qiita.com/FUKAch/items/00835ef2673247b023df)はコマンドライン引数について学びました。
今回はファイルI/O（Input/Output）について学びます。

# ファイル読み込み
ファイル読み込みに関しては今までに何度も例として行ってきましたが、ここで解説します。
以下は「test.txt」という名前のファイルの中身をString型として読み込み、表示するプログラムです。
```rust
use std::fs;
fn main() {
    let file_result = fs::read_to_string("test.txt");
    if let Ok(file) = file_result {
        println!("{}", file);
    }
}
```
ためしに「fileio」というプロジェクトを作り、直下に以下のような「test.txt」ファイルを作ります。
```txt:test.txt
This is a test document.
>> Hello, world!
>> 世界よ、おはよう！
```

この状態で実行すると、次のような出力が得られます。
```:出力結果
This is a test document.
>> Hello, world!
>> 世界よ、おはよう！
```
指定したファイルの中身を読めていることが分かります。

また、`std::fs`をインクルードしていますが、これはファイル操作のライブラリになります。ファイル操作のプログラムを書く場合は基本的にこれをインクルードします。

`read_to_string`関数は、Result型を返します。読み込めれば、String型のOkを、読み込めなければErrorを渡します。ですので、`if let`を用いてOkの場合のみ判定して、中身の文字列を取り出しています。
このプログラムの場合、読み込めなければ何もせずプログラムが終了します。

# ファイル書き込み
ここではフィボナッチ数列をファイルに書き込んでみます。
ファイル書き込みには、`BufWriter`か`write_all`メソッドを使用します。

今回は最初に`BufWriter`を使ってフィボナッチ数列をファイルに書き込み、最後に`write_all`を使った場合を紹介します。

まずは「fibonacci.txt」というファイルに「Fibonacci sequence」と書き込んでみましょう。
```rust
use std::fs;
use std::io::{self, Write};

fn main() {
    //"fibonacci.txt"を（上書き）作成する
    let fp = fs::File::create("fibonacci.txt");
    if let Ok(file) = fp {
        //BufWriteを利用して少しずつ書き込む
        let mut writer = io::BufWriter::new(file);

        //文字列をバイナリデータにしてwriteで書き込む
        let bytes = b"Fibonacci sequence\n";
        writer.write(bytes).unwrap();
    } else {
        eprintln!("Failed to create file")
    }
}
```
`io::BuffWriter`を使用するために、`std::io`をインクルードし、`write`を使用するために`std::io::Write`をインクルードします。
ここで、これらをまとめて記述すると、`use std::io::{self, Write}`となります。

また、`write`ではバイナリデータのみを出力できるので、文字列をバイナリデータに変換しています。

実行すると、「fibonacci.txt」というファイルができ、「Fibonacci sequence」と書かれました。

続いて、フィボナッチ数列を数値ごとに改行して書き込みます。
```diff_rust
use std::fs;
use std::io::{self, Write};

fn main() {
    //"fibonacci.txt"を（上書き）作成する
    let fp = fs::File::create("fibonacci.txt");
    if let Ok(file) = fp {
        //BufWriteを利用して少しずつ書き込む
        let mut writer = io::BufWriter::new(file);
        //文字列をバイナリデータにしてwriteで書き込む
        let bytes = b"Fibonacci sequence\n";
        writer.write(bytes).unwrap();
+       for i in 1..=100 {
+           let bytes = fib(i).to_string();
+           writer.write(&bytes.as_bytes()).unwrap();
+           writer.write(b"\n").unwrap();
+       }
    } else {
        eprintln!("Failed to create file")
    }
}

+fn fib(n: u128) -> u128 {
+    if n < 2 { return n; }
+    let q = n / 2;
+    let fq = fib(9);
+    return match n % 2 {
+        0 => fq * (2 * fib(q - 1) + fq),
+        _ => fib(q + 1).pow(2) + fq.pow(2),
+    }
+}
```
関数fibは、フィボナッチ数列の特定番目の値を求めます。
main関数では、for文を使用して1~100番目までのフィボナッチ数を求め、その都度「write」を使ってバッファーに保存しています。バッファーに保存された内容が書き出されるタイミングは様々です。「flush()」を使用すると、その時点で書き出せます。
数値は一度文字列にしてから「as_bytes」を用いてバイナリに変換しています。

最後に、`write_all`を利用する方法を載せておきます。
```rust
use std::fs;
use std::io::Write;

fn main() {
    //"fibonacci.txt"を（上書き）作成する
    let fp = fs::File::create("fibonacci.txt");
    if let Ok(mut file) = fp {
        //文字列をバイナリデータにしてwriteで書き込む
        let bytes = b"Fibonacci sequence\n";
        file.write_all(bytes).unwrap();
        for i in 1..=100 {
            let bytes = fib(i).to_string();
            file.write_all(&bytes.as_bytes()).unwrap();
            file.write_all(b"\n").unwrap();
        }
    } else {
        eprintln!("Failed to create file")
    }
}

fn fib(n: u128) -> u128 {
    if n < 2 { return n; }
    let q = n / 2;
    let fq = fib(q);
    return match n % 2 {
        0 => fq * (2 * fib(q - 1) + fq),
        _ => fib(q + 1).pow(2) + fq.pow(2),
    }
}
```
BufWriterがないことに気付きましたか？
これは、writeとwrite_allの内部的な挙動の違いに理由があるので、詳しく説明しようとすると長くなります。
とりあえず、今回はBufWriterが無くても書き込めることを示しましたが、こんなに要素が大きく、多ければ、BufWriterは使ったほうが良いです。システムコール数が大幅に減るので、とても効率良くなります。

# おわりに
今回はファイルI/Oについて学びました。
次回は借用と参照について学びます
ご精読ありがとうございました。