参照Rust官网文档,编写一个命令行程序,用来熟悉一些功能:
- 命令行参数如何传输
实现目标:
传入两个参数字符串、文件,将文件中包含对应字符串的内容输出。
1、项目新建grrs
cargo new grrs
2、参数获取
参数的方式有多种:
a、利用标准库中的std::env::args()来实现,其中第一个条目(index 0)是程序名称:
let pattern = std::env::args().nth(1).expect("no pattern given");
let path = std::env::args().nth(2).expect("no path given");
b、使用Clap解析参数
用于解析命令行参数的最流行的库称为
clap. 它具有您期望的所有功能,包括对子命令的支持、shell 补全以及出色的帮助消息。
使用Clap作代码说明,简单方便利于工程项目。
2.1 添加Clap库依赖,打开Cargo.toml添加
[package]
name = "grrs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "4.4.6", features = ["derive"] }
2.2 编写代码
编辑src\main.rs下的文件:
#![allow(unused)]
use clap::Parser;
/// Search for a pattern in a file and display the lines that contain it.
#[derive(Parser)]
struct Cli {
/// The pattern to look for
pattern: String,
/// The path to the file to read
path: std::path::PathBuf,
}
fn main() {
let args = Cli::parse();
}
2.3 构建运行
构建:cargo build
构建过程中,下载包速度比较慢,可以参考设置cargo国内代理。
不传参运行: cargo run
传输运行:cargo run -- hello 1.text
3、打开文件查找内容
3.1 代码开搞
打开文件方式:
let content = std::fs::read_to_string(&args.path).expect("could not read file");
注意: 看到.expect这里的方法了吗?这是一个退出的快捷函数,当无法读取值(在本例中为输入文件)时,它将使程序立即退出。 此方式不够优美,我们后面小节优化。主要认知下expect的使用。
迭代文件,并查询字符串内容:
for line in content.lines() {
if line.contains(&args.pattern) {
println!("{}", line);
}
}
添加文件打开,字符串检索后代码:
#![allow(unused)]
use clap::Parser;
/// Search for a pattern in a file and display the lines that contain it.
#[derive(Parser)]
struct Cli {
/// The pattern to look for
pattern: String,
/// The path to the file to read
path: std::path::PathBuf,
}
fn main() {
let args = Cli::parse();
let content = std::fs::read_to_string(&args.path).expect("could not read file");
for line in content.lines() {
if line.contains(&args.pattern) {
println!("{}", line);
}
}
}
3.2 运行测试
cargo run -- main ./src/main.rs
4 了解下错误处理
4.1 错误处理的方式
调用函数处理异常行为的几种方式:
- 使用expect处理,程序将退出:
let content = std::fs::read_to_string(&args.path).expect("could not read file");
- 使用panic处理,程序将退出:
let result = std::fs::read_to_string(&args.path);
let content = match result {
Ok(content) => { content },
Err(error) => { panic!("Can't deal with {}, just exit here", error); }
};
println!("file content: {}", content);
- 使用return,程序利用返回值返回,
Ok(())是函数的默认返回值,表示“结果没问题,没有内容:
fn main() -> Result<(), Box<dyn std::error::Error>> {
let result = std::fs::read_to_string("test.txt");
let content = match result {
Ok(content) => { content },
Err(error) => { return Err(error.into()); }
};
println!("file content: {}", content);
Ok(())
}
- 使用快捷方式
match``panic!``match``return``?:
fn main() -> Result<(), Box<dyn std::error::Error>> {
let content = std::fs::read_to_string("test.txt")?;
println!("file content: {}", content);
Ok(())
}
使用第三方库anyhow,来实现:
use anyhow::{Context, Result};
fn main() -> Result<()> {
let path = "test.txt";
let content = std::fs::read_to_string(path)
.with_context(|| format!("could not read file `{}`", path))?;
println!("file content: {}", content);
Ok(())
}
4.2 使用anyhow优化代码
4.2.1 添加anyhow库依赖,打开Cargo.toml添加
[package]
name = "grrs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "4.4.6", features = ["derive"] }
anyhow = "1.0.75"
4.2.2 代码修改
#![allow(unused)]
use clap::Parser;
use anyhow::{Context, Result};
/// Search for a pattern in a file and display the lines that contain it.
#[derive(Parser)]
struct Cli {
/// The pattern to look for
pattern: String,
/// The path to the file to read
path: std::path::PathBuf,
}
fn main() -> Result<()> {
let args = Cli::parse();
let content = std::fs::read_to_string(&args.path)
.with_context(|| format!("could not read file `{}`", &args.path.display()))?;
for line in content.lines() {
if line.contains(&args.pattern) {
println!("{}", line);
}
}
Ok(())
}
4.2.3 构建运行
cargo build
cargo run -- main ./src/main.rs
测试错误的例子:cargo run -- main 123.txt
遗留问题:
std::fs::read_to_string的读取优化,把整个文件读入内存,可以使用BufReader优化。
代理设置方式: