read lines - Rust By Example
Description: Rust by Example (RBE) is a collection of runnable examples that illustrate various Rust concepts and standard libraries.

## Keyboard shortcuts

Press `←` or `→` to navigate between chapters

Press `S` or `/` to search in the book

Press `?` to show this help

Press `Esc` to hide this help

# `read lines`

## 単純なやり方

テキストファイルの行を読み込むのを、初心者が初めて実装した場合、以下のようになるでしょう。

    #![allow(unused)]
    fn main() {
    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()`メソッドはファイルの各行のイテレータを返すので、インラインでマップを実行し結果を収集することもできます。そうすると、より簡潔で読みやすい表現となります。

    #![allow(unused)]
    fn main() {
    use std::fs::read_to_string;

    fn read_lines(filename: &str) -> Vec<String> {
        read_to_string(filename)
            .unwrap()  // ファイル読み込みエラーの場合はパニックします。
            .lines()  // 文字列のスライスのイテレータに分割します。
            .map(String::from)  // スライスを文字列に変換します。
            .collect()  // ベクタにまとめます。
    }
    }

上の例では、`lines()`から返された`&str`をそれぞれ`to_string()`と`String::from`を使って、所有権のある`String`型に変換しなければならない点に注意してください。

## より効率的なやり方

ここでは、開いた`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") {
            // イテレータを消費し、Option型のStringを返します。
            for line in lines.map_while(Result::ok) {
                println!("{}", line);
            }
        }
    }

    // 出力は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`にするよりも効率的です。メモリ上に`String`を作ると、より大きなファイルを取り扱う際に、パフォーマンスの問題につながります。