Rust篇章二: 命令行程序,查找文件中字符串

246 阅读3分钟

参照Rust官网文档,编写一个命令行程序,用来熟悉一些功能:

  • 命令行参数如何传输

实现目标:

传入两个参数字符串、文件,将文件中包含对应字符串的内容输出。

1、项目新建grrs

cargo new grrs

image.png

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国内代理。 image.png

不传参运行: cargo run

image.png

传输运行:cargo run -- hello 1.text image.png

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

image.png

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

image.png

测试错误的例子:cargo run -- main 123.txt

image.png

遗留问题:

std::fs::read_to_string的读取优化,把整个文件读入内存,可以使用BufReader优化。

代理设置方式: