Rust之构建命令行程序(五):环境变量

开发环境

  • Windows 11
  • Rust 1.77.0 
  • VS Code 1.87.2

项目工程

这次创建了新的工程minigrep.

 使用环境变量

我们将通过添加一个额外的功能来改进minigrep:一个不区分大小写的搜索选项,用户可以通过环境变量打开该选项。我们可以将此功能设置为命令行选项,并要求用户在每次想要应用它时输入它,但通过将其设置为环境变量,我们允许用户设置一次环境变量,并在该终端会话中使他们的所有搜索不区分大小写。

为不区分大小写的search函数编写失败测试

我们首先添加一个新的search_case_insensitive函数,当环境变量有值时将调用该函数。我们将继续遵循TDD过程,所以第一步是再次编写一个失败的测试。我们将为新的search_case_insensitive函数添加一个新测试,并将我们的旧测试从one_result重命名为case_sensitive,以阐明两个测试之间的差异,如示例12-20所示。

文件名:src/lib.rs

#[cfg(test)]
mod tests {use super::*;#[test]fn case_sensitive() {let query = "duct";let contents = "\
Rust:
safe, fast, productive.
Pick three.
Duct tape.";assert_eq!(vec!["safe, fast, productive."], search(query, contents));}#[test]fn case_insensitive() {let query = "rUsT";let contents = "\
Rust:
safe, fast, productive.
Pick three.
Trust me.";assert_eq!(vec!["Rust:", "Trust me."],search_case_insensitive(query, contents));}
}

 示例12-20:为我们将要添加的不区分大小写的函数添加新的失败测试

请注意,我们还编辑了旧测试的contents。我们添加了一个新行,文本为“Duct tape”当我们以区分大小写的方式进行搜索时,使用不应该与查询“duct”匹配的大写D。以这种方式更改旧测试有助于确保我们不会意外破坏已经实现的区分大小写的搜索功能。这个测试现在应该通过了,并且在我们进行不区分大小写的搜索时应该会继续通过。

不区分大小写搜索的新测试使用“rUsT”作为查询。在我们即将添加的search_case_insensitive函数中,查询“Rust:”应该与包含大写R的行“rUsT:”匹配,并与行“Trust me”匹配。即使两者的大小写与查询不同。这是我们失败的测试,它将无法编译,因为我们还没有定义search_case_insensitive函数。随意添加一个总是返回空向量的框架实现,类似于我们对示例12-16中的search函数所做的那样,以查看测试编译和失败。

实现search_case_insensitive函数 

示例12-21所示的search_case_insensitive函数与search函数几乎相同。唯一的区别是我们将queryline都小写,因此无论输入参数的大小写如何,当我们检查该行是否包含查询时,它们都是相同的大小写。

文件名:src/lib.rs

pub fn search_case_insensitive<'a>(query: &str,contents: &'a str,
) -> Vec<&'a str> {let query = query.to_lowercase();let mut results = Vec::new();for line in contents.lines() {if line.to_lowercase().contains(&query) {results.push(line);}}results
}

 示例12-21:定义search_case_insensitive函数在比较查询和行之前将其小写

首先,我们将query字符串小写,并将其存储在同名的隐藏变量中。对查询调用to_lowercase是必要的,因此无论用户的查询是“rust”、“Rust”、“rUsT”还是“rust”,我们都将查询视为“RUST”,并且不区分大小写。虽然to_lowercase可以处理基本的Unicode,但它不会100%准确。如果我们正在编写一个真正的应用程序,我们会想在这里多做一点工作,但这一节是关于环境变量的,而不是Unicode,所以我们将把它留在这里。

请注意,query现在是String而不是字符串片段,因为调用to_lowercase会创建新数据而不是引用现有数据。例如,假设查询是“rUsT”:该字符串片段不包含供我们使用的小写ut,因此我们必须分配一个包含“rust”的新String。当我们现在将query作为参数传递给contains方法时,我们需要添加一个&符号,因为contains的签名被定义为接受一个字符串切片。

接下来,我们在line添加一个对to_lowercase的调用,以小写所有字符。现在我们已经将linequery转换为小写,无论查询的大小写如何,我们都将找到匹配项。 

让我们看看这个实现是否通过了测试: 

$ cargo testCompiling minigrep v0.1.0 (file:///projects/minigrep)Finished test [unoptimized + debuginfo] target(s) in 1.33sRunning unittests src/lib.rs (target/debug/deps/minigrep-9cd200e5fac0fc94)running 2 tests
test tests::case_insensitive ... ok
test tests::case_sensitive ... oktest result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00sRunning unittests src/main.rs (target/debug/deps/minigrep-9cd200e5fac0fc94)running 0 teststest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00sDoc-tests minigreprunning 0 teststest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

 太好了!他们通过了。现在,让我们从run函数中调用新的search_case_insensitive函数。首先,我们将向Config结构添加一个配置选项,以在区分大小写和不区分大小写的搜索之间切换。添加此字段将导致编译器错误,因为我们还没有在任何地方初始化此字段:

文件名:src/lib.rs

pub struct Config {pub query: String,pub file_path: String,pub ignore_case: bool,
}

 我们添加了包含布尔值的ignore_case字段。接下来,我们需要run函数来检查ignore_case字段的值,并使用它来决定是调用search函数还是search_case_insensitive函数,如示例12-22所示。这仍然无法编译。

文件名:src/lib.rs

pub fn run(config: Config) -> Result<(), Box<dyn Error>> {let contents = fs::read_to_string(config.file_path)?;let results = if config.ignore_case {search_case_insensitive(&config.query, &contents)} else {search(&config.query, &contents)};for line in results {println!("{line}");}Ok(())
}

清单12-22:根据config.ignore_case中的值调用searchsearch_case_insensitive

最后,我们需要检查环境变量。处理环境变量的函数在标准库中的env模块中,因此我们将该模块纳入src/lib.rs的顶部。然后我们将使用env模块中的var函数来检查是否为名为IGNORE_CASE的环境变量设置了任何值,如示例12-23所示。 

文件名:src/lib.rs 

use std::env;
// --snip--impl Config {pub fn build(args: &[String]) -> Result<Config, &'static str> {if args.len() < 3 {return Err("not enough arguments");}let query = args[1].clone();let file_path = args[2].clone();let ignore_case = env::var("IGNORE_CASE").is_ok();Ok(Config {query,file_path,ignore_case,})}
}

清单12-23:检查名为IGNORE_CASE的环境变量中的任何值

在这里,我们创建了一个新变量ignore_case。为了设置其值,我们调用env::var函数并向其传递IGNORE_CASE环境变量的名称。如果环境变量设置为任意值,env::var函数返回的Result将是成功的Ok变量,该变量包含环境变量的值。如果未设置环境变量,它将返回Err变量。 

我们对Result使用is_ok方法来检查是否设置了环境变量,这意味着程序应该进行不区分大小写的搜索。如果IGNORE_CASE环境变量未设置为任何值,is_ok将返回false,程序将执行区分大小写的搜索。我们不关心环境变量的值,只关心它是设置的还是未设置的,因此我们检查的是is_ok,而不是使用unwrapexpect或我们在Result上看到的任何其他方法。 

我们将ignore_case变量中的值传递给Config实例,这样run函数就可以读取该值并决定是调用search_case_insensitive还是search,如示例12-22所示。 

完整源码

文件名:src/lib.rs 

pub mod cfg {use std::error::Error;use std::env;use std::fs;pub struct Config {pub query: String,pub file_path: String,pub ignore_case: bool,}impl Config {pub fn build(args: &[String]) -> Result<Config, &'static str> {if args.len() < 3 {return Err("not enough arguments");}let query = args[1].clone();let file_path = args[2].clone();let ignore_case = env::var("IGNORE_CASE").is_ok();Ok(Config {query,file_path,ignore_case,})}}#[cfg(test)]
mod tests {use super::*;#[test]fn case_sensitive() {let query = "duct";let contents = "\Rust:safe, fast, productive.Pick three.Duct tape.";assert_eq!(vec!["safe, fast, productive."], search(query, contents));}#[test]fn case_insensitive() {let query = "rUsT";let contents = "\Rust:safe, fast, productive.Pick three.Trust me.";assert_eq!(vec!["Rust:", "Trust me."],search_case_insensitive(query, contents));}
}pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {let mut results = Vec::new();for line in contents.lines() {if line.contains(query) {results.push(line);}}results}pub fn search_case_insensitive<'a>(query: &str,contents: &'a str,) -> Vec<&'a str> {let query = query.to_lowercase();let mut results = Vec::new();for line in contents.lines() {if line.to_lowercase().contains(&query) {results.push(line);}}results}pub fn run(config: Config) -> Result<(), Box<dyn Error>> {let contents = fs::read_to_string(config.file_path)?;let results = if config.ignore_case {search_case_insensitive(&config.query, &contents)} else {search(&config.query, &contents)};for line in results {println!("{line}");}Ok(())}
}

文件名:src/main.rs  

use std::env;
use std::process;
use minigrep::cfg;fn main() {let args: Vec<String> = env::args().collect();let config = cfg::Config::build(&args).unwrap_or_else(|err| {println!("Problem parsing arguments: {err}");process::exit(1);});println!("Searching for {}", config.query);println!("In file {}", config.file_path);if let Err(e) = cfg::run(config) {println!("Application error: {e}");process::exit(1);} 
}

让我们试一试吧!首先,我们将在不设置环境变量的情况下运行我们的程序,并使用to查询,该查询应匹配包含全部小写单词“to”的任何行: 

$ cargo run -- to poem.txtCompiling minigrep v0.1.0 (file:///projects/minigrep)Finished dev [unoptimized + debuginfo] target(s) in 0.0sRunning `target/debug/minigrep to poem.txt`
Are you nobody, too?
How dreary to be somebody!

 看来这仍然有效!现在,让我们运行程序,将IGNORE_CASE设置为1,但仍然使用to

$ IGNORE_CASE=1 cargo run -- to poem.txt

 如果您使用的是PowerShell,则需要设置环境变量并作为单独的命令运行程序:

PS> $Env:IGNORE_CASE=1; cargo run -- to poem.txt

 这将使IGNORE_CASE在shell会话的剩余部分持续存在。可以使用Remove-Item cmdlet取消设置它:

PS> Remove-Item Env:IGNORE_CASE

 我们应该得到包含“to”的行,这些行可能包含大写字母:

Are you nobody, too?
How dreary to be somebody!
To tell your name the livelong day
To an admiring bog!

 太好了,我们还得到了包含“To”的行!我们的minigrep程序现在可以进行由环境变量控制的不区分大小写的搜索。现在您知道了如何使用命令行参数或环境变量来管理选项集。

一些程序允许相同配置的参数和环境变量。在这些情况下,程序会决定其中一个优先。对于您自己的另一个练习,尝试通过命令行参数或环境变量来控制大小写。如果程序运行时一个设置为区分大小写,一个设置为忽略大小写,请确定是命令行参数还是环境变量优先。 

std::env模块包含许多更有用的处理环境变量的特性:查看其文档以了解可用的特性。 

本章重点

  • 环境变量的意义
  • 编写不区分大小写的search函数
  • 不区分大小写search函数的实现思路
  • 通过添加环境变量的方式实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/282214.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux 升级openssl1.1.1w 亲测记录

下载好openssl源码包,解压到指定目录下 tar -xzvf openssl-1.1.1w.tar.gz -C /usr/localcd openssl-1.1.1w/*预编译、编译、安装*/./config --prefix/usr/local/openssl sharedmake && make install备份配置系统中原有的文件、创建软链接、动态库查找路径配置文件 ld.s…

SpringBoot实战(二十七)集成WebFlux

目录 一、WebFlux1.1 定义1.2 WebFlux 与 Spring MVC 区别 二、代码实现2.1 Maven 配置2.2 暴露 RESTful API 接口的方式方式一&#xff1a;基于注解的控制器方式二&#xff1a;函数式路由器&#xff08;Functional Endpoints&#xff09; 2.3 测试Service2.4 测试ServiceImpl2…

【Charles如何对手机APP进行抓包和弱网测试】

一、Charles对APP抓包 1、前提条件&#xff1a; 1&#xff09;电脑上必须安装Charles工具&#xff0c;如没有安装可参考&#xff1a;【Charles抓包工具下载安装详细操作步骤】-CSDN博客 2&#xff09;手机和电脑必须在同一个局域网内&#xff08;连接同一个WiFi&#xff09;…

ng发布静态资源 发布项目 发布数据

描述&#xff1a;把一个项目或者数据发布出来&#xff0c;通过http的形式访问&#xff0c;比如发布一个js文件&#xff0c;用http://localhost:6060/data/jquery/jquery.min.js访问。 步骤&#xff1a;配置nginx.conf文件&#xff0c;nginx.conf位于conf目录下&#xff0c;在se…

P2822 [NOIP2016 提高组] 组合数问题题解

题目 组合数表示的是从n个物品中选出m个物品的方案数。举个例子&#xff0c;从(1,2,3) 三个物品中选择两个物品可以有 (1,2),(1,3),(2,3)这三种选择方法。根据组合数的定义&#xff0c;我们可以给出计算组合数的一般公式&#xff1a; 其中n!12⋯n&#xff1b;特别地&#xff0…

Java 在PDF中插入页眉、页脚

在处理PDF文档时&#xff0c;有时需要为文档中的每一页添加页眉和页脚&#xff0c;以包含一些有用的信息&#xff0c;如文档标题、章节名称、日期、页码等。对于需要自动化处理的场景&#xff0c;或者需要在大量文档中添加一致的页眉和页脚&#xff0c;可以通过编程的方式来实现…

QGraphicsView的使用,view坐标,scene坐标,item坐标

Graphics View绘图构架 QGraphicsScene&#xff08;场景&#xff09;&#xff1a;可以管理多个图形项QGraphicsItem&#xff08;图形项&#xff09;&#xff1a;也就是图元&#xff0c;支持鼠标事件响应。QGraphicsView&#xff08;视图&#xff09;&#xff1a;关联场景可以让…

【数据库系统】数据库完整性和安全性

第六章 数据库完整性和安全性 基本内容 安全性&#xff1b;完整性&#xff1b;数据库恢复技术&#xff1b;SQL Server的数据恢复机制&#xff1b; 完整性 实体完整性、参照完整性、用户自定义完整性 安全性 身份验证权限控制事务日志&#xff0c;审计数据加密 数据库恢复 冗余…

Redis学习二--常见问题及处理

基本概念 Redis基本概念数据结构 机制 持久化机制&#xff1a; RDB(内存快照)&#xff1a;某一时刻的内存快照以二进制的方式写入磁盘&#xff0c;可以手动触发和自动触发。 优点&#xff1a;生成文件小&#xff0c;恢复速度快&#xff0c;适用于灾难恢复。 缺点&#xff1a…

关于Zookeeper分布式锁

背景 之前说到分布式锁的实现有三种 1、基于数据库实现的分布式锁 2、Redis分布式锁 3、Zookeeper分布式锁 前者redis分布式锁博客已具体介绍&#xff0c;此博客最终决定补齐关于Zookeeper分布式锁的实现原理。 简述 Zoopkeeper&#xff0c;它是一个为分布式的协调服务&…

固态继电器(SSR)您需要了解的一切

固态继电器&#xff08;也称SSR&#xff0c;SS继电器或SSR开关&#xff09;是一种集成的非接触式电子开关设备&#xff0c;由集成电路&#xff08;IC&#xff09;和分立组件紧密组装而成。处于现代电气应用的最前沿&#xff0c;与机电同类产品相比&#xff0c;具有许多优势。本…

重学SpringBoot3-Profiles介绍

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-Profiles介绍 Profiles简介如何在Spring Boot中使用Profiles定义Profiles激活ProfilesIDEA设置active profile使用Profile-specific配置文件 条件化Bean…

flex布局

文章目录 1. 概念2. 和浮动的区别3. 伸缩容器和伸缩项目3.1. 伸缩容器3.2. 伸缩项目 4. 主轴与侧轴5. 主轴属性6. 纵轴属性6.1. align-self 示例 7. flex 实现水平垂直居中7.1. 方法一7.2. 方法二 8. 伸缩性8.1. flex-basis8.2. flex-shrink8.3. flex-grow&#xff08;伸&#…

如何做人才运营战略?

招聘人才和人才获取是同义词&#xff0c;但它们并不相同。招聘是大多数雇主的短期解决方案&#xff0c;而人才获取是一个长期解决方案。 企业要想改善企业文化朝着统一的愿景努力&#xff0c;就需要关注长期规划。 人才获取vs人才招聘 招聘是为了填补空缺&#xff0c;人才获取…

在服务器(Ubuntu20.04)安装用户级别的cuda11.8

1、cuda11.8的下载 首先在cuda官网下载我们需要的cuda版本&#xff0c;这里我下载的是cuda11.8&#xff08;我的最高支持cuda12.0&#xff09; 这里我直接使用wget命令下载不了&#xff0c;于是我直接在浏览器输入后面的链接下载到本地&#xff0c;之后再上传至服务器的&am…

SpringBoot2.7集成Swagger3

Swagger2已经在17年停止维护了&#xff0c;取而代之的是 Swagger3&#xff08;基于openApi3&#xff09;&#xff0c;所以新项目要尽量使用Swagger3. Open API OpenApi是业界真正的 api 文档标准&#xff0c;其是由 Swagger 来维护的&#xff0c;并被linux列为api标准&#x…

Stable Diffusion WebUI 生成参数:宽度/高度/生成批次/每批数量/提示词相关性/随机种子

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里。 大家好,我是水滴~~ 本文将继续了解 Stable Diffusion WebUI 的生成参数,主要内容有:宽度、高度、生成批次、每批数量、提示词相关性、随机种子。希望能对你有所帮助。 文章目录 宽度(Width)和高度(Height)…

前端实例:页面布局1(后端数据实现)

效果图 注&#xff1a;这里用到后端语言php&#xff08;页面是.php文件&#xff09;,提取纯html也可以用 inemployee_index.php <?php include(includes/session.inc); $Title _(内部员工首页); $ViewTopic 内部员工首页; $BookMark 内部员工首页; include(includes/…

异地组网有哪些实现方式?为什么要选择SD-WAN?

建立跨地域的异地组网在当前数字化时代变得越来越重要&#xff0c;主要是因为企业业务的不断扩展和多样化。异地网络连接不仅有助于改善内部通信效率&#xff0c;还能提高数据处理能力和业务连续性。那么&#xff0c;到底有哪些方式可以实现异地组网呢&#xff1f;应该选择哪种…

【Linux】进程

本文主要介绍了进程的相关理解&#xff1a;查看进程、进程状态、进程的优先级、环境变量、进程地址空间、Linux内核进程调度队列。 目录 冯诺依曼体系结构 操作系统 进程 查看进程 几点预备小知识 进程创建的代码方式 为什么要创建子进程 样例代码&#xff1a;依次创建多…