web-dev-qa-db-ja.com

Rustでのユーザー入力の読み方

編集者注:この質問は、Rustその前日Rust 1.0ですが、一般的な概念はRust = 1.0。

トークナイザーを作成するつもりです。ユーザーが入力するすべての行を読み取り、ユーザーが押すと読み取りを停止する必要があります ctrl-D

私はあちこち検索しました 1つの例 on Rust IOこれはコンパイルさえしません。ioモジュールのドキュメントにより、read_line()関数はReaderUtilインターフェイスの一部ですが、stdin()は代わりにReaderを返します。

私が望むコードは、C++では基本的に次のようになります

vector<string> readLines () {
    vector<string> allLines;
    string line;

    while (cin >> line) {
        allLines.Push_back(line);
    }

    return allLines;
}
42
Jimmy Lu

編集者注:この回答は、以前のRust 1.0です。最新のソリューションについては、他の回答をご覧ください。

Rust 0.4では、ReaderUtil traitを使用して_read_line_関数にアクセスします。値を明示的に特性タイプにキャストする必要があることに注意してください(_reader as io::ReaderUtil_

_fn main() {
        let mut allLines = ~[];
        let reader = io::stdin();

        while !reader.eof() {
                allLines.Push((reader as io::ReaderUtil).read_line());
        }

        for allLines.each |line| {
                io::println(fmt!("%s", *line));
        }
}
_
0
Bilal Husain

Rust 1.x( ドキュメント を参照):

use std::io;
use std::io::prelude::*;

fn main() {
    let stdin = io::stdin();
    for line in stdin.lock().lines() {
        println!("{}", line.unwrap());
    }
}

Rust 0.10–0.12( ドキュメント を参照):

use std::io;

fn main() {
    for line in io::stdin().lines() {
        print!("{}", line.unwrap());
    }
}

Rust 0.9( .9ドキュメント を参照):

use std::io;
use std::io::buffered::BufferedReader;

fn main() {
    let mut reader = BufferedReader::new(io::stdin());
    for line in reader.lines() {
        print(line);
    }
}

Rust 0.8:

use std::io;

fn main() {
    let lines = io::stdin().read_lines();
    for line in lines.iter() {
        println(*line);
    }
}

Rust 0.7:

use std::io;

fn main() {
    let lines = io::stdin().read_lines();
    for lines.iter().advance |line| {
        println(*line);
    }
}
141
robinst

2015年4月17日の時点で、mozillaのmdcoxからRust irc。

use std::io;

fn main() {
    let mut stdin = io::stdin();
    let input = &mut String::new();

    loop {
        input.clear();
        stdin.read_line(input);
        println!("{}", input);
    }
}
13
gaigepr

問題は、stdinから行を読み取り、ベクトルを返すことです。オンRust 1.7:

fn readlines() -> Vec<String> {
    use std::io::prelude::*;
    let stdin = std::io::stdin();
    let v = stdin.lock().lines().map(|x| x.unwrap()).collect();
    v
}
3
Aethanyc

私が考えることができるいくつかの方法があります。

すべての入力を単一のStringに読み取ります

let mut input = String::new();
io::stdin().read_to_end(&mut input);

行をVectorに読み込みます。これは、行の読み取りが失敗したときにpanicではなく、その失敗した行をスキップします。

let stdin = io::stdin();
let locked = stdin.lock();
let v: Vec<String> = locked.lines().filter_map(|line| line.ok()).collect();

さらに、それを解析したい場合:

文字列に読み込んだ後、これを行います。 FromIteratorを実装する他のコレクションに解析できます。コレクションに含まれる要素もFromStrを実装する必要があります。特性制約が満たされる限り、VecをCollection:FromIteratorCollection<T: FromStr>に変更できます

let v: Vec<i32> = "4 -42 232".split_whitespace().filter_map(|w| w.parse().ok()).collect();

また、StdinLockでも使用できます

let vv: Vec<Vec<i32>> = locked
    .lines()
    .filter_map(|l|
        l.ok().map(|s|
            s.split_whitespace().filter_map(|Word| Word.parse().ok()).collect()
        )
    )
    .collect();
3
Dulguun Otgon

編集者注:この回答は、以前のRust 1.0です。最新のソリューションについては、他の回答をご覧ください。

ええと...多くの試行錯誤の後、私は解決策を見つけました。

私はまだより良い解決策を見たいので、自分の解決策を受け入れるつもりはありません。

以下のコードは、ユーザーが入力した内容を正確に出力します。

mod tokenizer {

pub fn read () -> ~[int] {
    let reader = io::stdin();
    let mut bytes: ~[int] = ~[];

    loop {
        let byte: int = reader.read_byte();
        if byte < 0 {
            return bytes;
        }
        bytes += [byte];
    }
}

}

fn main () {
    let bytes: ~[int] = tokenizer::read();
    for bytes.each |byte| {
        io::print(#fmt("%c", *byte as char));
    }
}
2
Jimmy Lu

Rust 1.0以降では、 lines メソッドを std::io::BufRead を実装するもので使用できます。入力の行のイテレータを取得するための特性。 read_line を使用することもできますが、イテレータを使用する方が必要な可能性が高くなります。イテレータを使用した質問;詳細な説明については、以下を参照してください( playground link

use std::io;
use std::io::prelude::*;

pub fn read_lines() -> Vec<String> {
    let stdin = io::stdin();
    let stdin_lock = stdin.lock();
    let vec = stdin_lock.lines().filter_map(|l| l.ok()).collect();

    vec
}

そして、これは質問のC++バージョンに似ていますが、Rust( playground ))でこれを行う慣用的な方法ではありません:

use std::io;
use std::io::prelude::*;

pub fn read_lines() -> Vec<String> {
    let mut vec = Vec::new();
    let mut string = String::new();

    let stdin = io::stdin();
    let mut stdin_lock = stdin.lock();

    while let Ok(len) = stdin_lock.read_line(&mut string) {
        if len > 0 {
           vec.Push(string);
           string = String::new();
        } else {
            break
        }
    }

    vec
}

lines()またはread_line()を呼び出すために必要なBufReadを実装するものを取得するには、 std::io::stdin() を呼び出してハンドルを取得します標準入力に移動し、その結果に対して lock() を呼び出して、標準入力ストリームの排他制御を取得します(BufReadを取得するには排他制御が必要です。それ以外の場合、2つのスレッドが一度に標準入力から読み込んでいる場合、バッファリングは任意の結果を生成する可能性があります)。

結果をVec<String>に収集するには、イテレータで collect メソッドを使用できます。 lines()Result<String>のイテレータを返すため、行を読み取れなかったエラーの場合を処理する必要があります。この例では、エラーをスキップするfilter_mapのエラーを無視します。

C++のようなバージョンはread_lineを使用します。これは、指定された文字列に読み取り行を追加し、その文字列をVecにプッシュします。それを行うときに文字列の所有権をVecに転送し、read_linestringに追加し続けるため、各ループに新しい文字列を割り当てる必要があります。 (これは、同じ文字列が共有されているため、すべての行を蓄積し続ける問題の元のC++バージョンのバグのようです)。 while let を使用して、エラーが発生するまで読み取りを続け、入力の終わりを示すゼロバイトを読み取った場合は中断します。

2
Brian Campbell

これは私が思いついたものです(#Rust IRC irc.mozilla.orgのチャンネル)の友好的な人々の助けを借りて):

use core::io::ReaderUtil;

fn read_lines() -> ~[~str] {
    let mut all_lines = ~[];

    for io::stdin().each_line |line| {
        // each_line provides us with borrowed pointers, but we want to put
        // them in our vector, so we need to *own* the lines
        all_lines.Push(line.to_owned());
    }

    all_lines
}

fn main() {
    let all_lines = read_lines();

    for all_lines.eachi |i, &line| {
        io::println(fmt!("Line #%u: %s", i + 1, line));
    }
}

そして、それが機能することの証明:)

$ rustc readlines.rs
$ echo -en 'this\nis\na\ntest' | ./readlines
Line #1: this
Line #2: is
Line #3: a
Line #4: test
1
Scott Olson