IO 操作

std 是 Rust 的標準庫,其中 std::io 提供了多種 IO 操作的工具。

stdin.lock().lines() 解釋

  • io::stdin() 給你一個標準輸入的 handle (控制代碼)。
  • .lock() 取得對標準輸入的鎖定(防止重複的系統調用並避免跨線程的資源競爭)。
  • .lines() (來自 BufRead trait) 返回一個輸入行的迭代器。每次迭代產生一個 Result<String, io::Error>String 包含單行內容,不包括末尾的換行符。

在程式設計中,控制代碼(handle)是對資源的抽象參照,當一個應用程式要參照其他系統(如資料庫、作業系統)所管理的主記憶體塊或對象時,可以使用控制代碼。

簡單的使用範例

use std::io::{self, BufRead};

fn main() {
    let stdin = io::stdin(); // 取得標準輸入,也就是 `Stdin`
    let mut handle = stdin.lock(); // 取得 `StdinLock`

    for line_result in handle.lines() {
        match line_result {
            Ok(line) => {
                if line.trim() == "quit" {
                    break;
                }

                println!("You typed: {}", line);
            }
            Err(err) => {
                eprintln!("Error reading line: {}", err);
                break;
            }
        }
    }
}

執行上述這段程式碼,每輸入一行後按下 Enter,它會讀取該行並執行對應的程式碼,直到你輸入 quit

小細節

  • 所有權:每個 Ok(String) 都是新分配的字串,並由你擁有;它不包括末尾的 \n
  • 錯誤:你必須處理 Err(io::Error);在許多範例中,人們在原型設計時使用 ?unwrap()
  • 何時結束:當用戶輸入 quit 會中斷循環。
  • 性能:使用 .lock() 配合 BufRead 進行逐行讀取,是很常用且高效的做法。

使用 .filter_map() 跳過空行

use std::io::{self, BufRead};

fn main() {
    let stdin = io::stdin();
    let mut non_empty = stdin
        .lock()
        .lines()
        .filter_map(|res| res.ok())
        .map(|s| s.trim().to_string())
        .filter(|s| !s.is_empty());

    // 第一個非空行作為整數
    let n: usize = non_empty
        .next()
        .and_then(|s| s.parse().ok())
        .unwrap_or(0);

    println!("N = {}", n);
}

慣用的解析模式

use std::io::{self, BufRead};

fn read_n_numbers() -> io::Result<Vec<i64>> {
    let stdin = io::stdin();
    let mut lines = stdin.lock().lines();

    // 讀取第一個非空行作為 N
    let n: usize = loop {
        match lines.next() {
            Some(Ok(l)) if !l.trim().is_empty() => match l.trim().parse() {
                Ok(v) => break v,
                Err(_) => return Err(io::Error::new(io::ErrorKind::InvalidData, "bad N")),
            },
            Some(Ok(_)) => continue, // 跳過空行
            Some(Err(e)) => return Err(e),
            None => return Err(io::Error::from(io::ErrorKind::UnexpectedEof)),
        }
    };

    // 正好讀取 N 行
    let mut out = Vec::with_capacity(n);
    for _ in 0..n {
        let line = match lines.next() {
            Some(Ok(l)) => l,
            Some(Err(e)) => return Err(e),
            None => return Err(io::Error::from(io::ErrorKind::UnexpectedEof)),
        };
        let v: i64 = line.trim().parse().map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "bad int"))?;
        out.push(v);
    }

    Ok(out)
}

這與你的程式使用的模式相同:從 stdin.lock().lines() 創建一次迭代器,然後用 next() 從中提取行,並相應地處理 Ok/Err/None

參考資料


This site uses Just the Docs, a documentation theme for Jekyll.