Rust 接受命令行參數(shù)

2023-03-22 15:10 更新
ch12-01-accepting-command-line-arguments.md
commit 0f87daf683ae3de3cb725faecb11b7e7e89f0e5a

一如既往使用 cargo new 新建一個(gè)項(xiàng)目,我們稱之為 minigrep 以便與可能已經(jīng)安裝在系統(tǒng)上的 grep 工具相區(qū)別:

$ cargo new minigrep
     Created binary (application) `minigrep` project
$ cd minigrep

第一個(gè)任務(wù)是讓 minigrep 能夠接受兩個(gè)命令行參數(shù):文件名和要搜索的字符串。也就是說我們希望能夠使用 cargo run、要搜索的字符串和被搜索的文件的路徑來運(yùn)行程序,像這樣:

$ cargo run searchstring example-filename.txt

現(xiàn)在 cargo new 生成的程序忽略任何傳遞給它的參數(shù)。Crates.io 上有一些現(xiàn)成的庫(kù)可以幫助我們接受命令行參數(shù),不過我們正在學(xué)習(xí)這些內(nèi)容,讓我們自己來實(shí)現(xiàn)一個(gè)。

讀取參數(shù)值

為了確保 minigrep 能夠獲取傳遞給它的命令行參數(shù)的值,我們需要一個(gè) Rust 標(biāo)準(zhǔn)庫(kù)提供的函數(shù),也就是 std::env::args。這個(gè)函數(shù)返回一個(gè)傳遞給程序的命令行參數(shù)的 迭代器iterator)。我們會(huì)在 第十三章 全面的介紹它們。但是現(xiàn)在只需理解迭代器的兩個(gè)細(xì)節(jié):迭代器生成一系列的值,可以在迭代器上調(diào)用 collect 方法將其轉(zhuǎn)換為一個(gè)集合,比如包含所有迭代器產(chǎn)生元素的 vector。

使用示例 12-1 中的代碼來讀取任何傳遞給 minigrep 的命令行參數(shù)并將其收集到一個(gè) vector 中。

文件名: src/main.rs

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    println!("{:?}", args);
}

示例 12-1:將命令行參數(shù)收集到一個(gè) vector 中并打印出來

首先使用 use 語句來將 std::env 模塊引入作用域以便可以使用它的 args 函數(shù)。注意 std::env::args 函數(shù)被嵌套進(jìn)了兩層模塊中。正如 第七章 講到的,當(dāng)所需函數(shù)嵌套了多于一層模塊時(shí),通常將父模塊引入作用域,而不是其自身。這便于我們利用 std::env 中的其他函數(shù)。這比增加了 use std::env::args; 后僅僅使用 args 調(diào)用函數(shù)要更明確一些,因?yàn)?nbsp;args 容易被錯(cuò)認(rèn)成一個(gè)定義于當(dāng)前模塊的函數(shù)。

args 函數(shù)和無效的 Unicode

注意 std::env::args 在其任何參數(shù)包含無效 Unicode 字符時(shí)會(huì) panic。如果你需要接受包含無效 Unicode 字符的參數(shù),使用 std::env::args_os 代替。這個(gè)函數(shù)返回 OsString 值而不是 String 值。這里出于簡(jiǎn)單考慮使用了 std::env::args,因?yàn)?nbsp;OsString 值每個(gè)平臺(tái)都不一樣而且比 String 值處理起來更為復(fù)雜。

在 main 函數(shù)的第一行,我們調(diào)用了 env::args,并立即使用 collect 來創(chuàng)建了一個(gè)包含迭代器所有值的 vector。collect 可以被用來創(chuàng)建很多類型的集合,所以這里顯式注明 args 的類型來指定我們需要一個(gè)字符串 vector。雖然在 Rust 中我們很少會(huì)需要注明類型,然而 collect 是一個(gè)經(jīng)常需要注明類型的函數(shù),因?yàn)?Rust 不能推斷出你想要什么類型的集合。

最后,我們使用調(diào)試格式 :? 打印出 vector。讓我們嘗試分別用兩種方式(不包含參數(shù)和包含參數(shù))運(yùn)行代碼:

$ cargo run
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished dev [unoptimized + debuginfo] target(s) in 0.61s
     Running `target/debug/minigrep`
["target/debug/minigrep"]
$ cargo run needle haystack
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished dev [unoptimized + debuginfo] target(s) in 1.57s
     Running `target/debug/minigrep needle haystack`
["target/debug/minigrep", "needle", "haystack"]

注意 vector 的第一個(gè)值是 "target/debug/minigrep",它是我們二進(jìn)制文件的名稱。這與 C 中的參數(shù)列表的行為相匹配,讓程序使用在執(zhí)行時(shí)調(diào)用它們的名稱。如果要在消息中打印它或者根據(jù)用于調(diào)用程序的命令行別名更改程序的行為,通??梢苑奖愕卦L問程序名稱,不過考慮到本章的目的,我們將忽略它并只保存所需的兩個(gè)參數(shù)。

將參數(shù)值保存進(jìn)變量

打印出參數(shù) vector 中的值展示了程序可以訪問指定為命令行參數(shù)的值。現(xiàn)在需要將這兩個(gè)參數(shù)的值保存進(jìn)變量這樣就可以在程序的余下部分使用這些值了。讓我們?nèi)缡纠?12-2 這樣做:

文件名: src/main.rs

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();

    let query = &args[1];
    let filename = &args[2];

    println!("Searching for {}", query);
    println!("In file {}", filename);
}

示例 12-2:創(chuàng)建變量來存放查詢參數(shù)和文件名參數(shù)

正如之前打印出 vector 時(shí)所所看到的,程序的名稱占據(jù)了 vector 的第一個(gè)值 args[0],所以我們從索引 1 開始。minigrep 獲取的第一個(gè)參數(shù)是需要搜索的字符串,所以將其將第一個(gè)參數(shù)的引用存放在變量 query 中。第二個(gè)參數(shù)將是文件名,所以將第二個(gè)參數(shù)的引用放入變量 filename 中。

我們將臨時(shí)打印出這些變量的值來證明代碼如我們期望的那樣工作。使用參數(shù) test 和 sample.txt 再次運(yùn)行這個(gè)程序:

$ cargo run test sample.txt
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished dev [unoptimized + debuginfo] target(s) in 0.0s
     Running `target/debug/minigrep test sample.txt`
Searching for test
In file sample.txt

好的,它可以工作!我們將所需的參數(shù)值保存進(jìn)了對(duì)應(yīng)的變量中。之后會(huì)增加一些錯(cuò)誤處理來應(yīng)對(duì)類似用戶沒有提供參數(shù)的情況,不過現(xiàn)在我們將忽略他們并開始增加讀取文件功能。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)