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 test
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished test [unoptimized + debuginfo] target(s) in 1.33s
     Running unittests src/lib.rs (target/debug/deps/minigrep-9cd200e5fac0fc94)

running 2 tests
test tests::case_insensitive ... ok
test tests::case_sensitive ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running unittests src/main.rs (target/debug/deps/minigrep-9cd200e5fac0fc94)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests minigrep

running 0 tests

test 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.txt
   Compiling minigrep v0.1.0 (file:///projects/minigrep)
    Finished dev [unoptimized + debuginfo] target(s) in 0.0s
     Running `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函数的实现思路
  • 通过添加环境变量的方式实现

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python模块与包管理使用pip与virtualenv【第151篇—模块与包管理】

Python模块与包管理&#xff1a;使用pip与virtualenv 在Python开发中&#xff0c;模块和包管理是至关重要的&#xff0c;它们使得代码的组织、重用和共享变得更加简单和高效。本文将介绍两个Python生态系统中最常用的工具&#xff1a;pip和virtualenv。通过这些工具&#xff0…

UE5 C++ 3D血条 响应人物受伤 案例

一.3Dwidget 1.创建C Userwidget的 MyHealthWidget&#xff0c;声明当前血量和最大血量 UCLASS() class PRACTICEC_API UMyHealthWidget : public UUserWidget {GENERATED_BODY() public:UPROPERTY(EditAnywhere,BlueprintReadWrite,Category "MyWidget")float C…

java中异常类

异常 异常体系继承结构 Throwable类是 Java 语言中所有错误或异常的超类&#xff0c;只有当对象是此类&#xff08;或其子类之一&#xff09;的实例时&#xff0c;才能通过 Java 虚拟机或者 Java throw 语句抛出。     异常是对象&#xff0c;而对象都采用类来定义。异常的…

C语言之strcspn用法实例(八十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

使用jupyter-Python进行模拟股票分析

tushare财经数据接口包 pip install tushare作用&#xff1a;提供相关指定的财经数据 需求&#xff1a;股票分析 使用tushare包获取某股票的历史行情数据 输出该股票所有收盘比开盘上涨3%以上的日期 输出该股票所有开盘比前日收盘跌幅超过2%的日期 假如我从2015年1月1日开…

Ubuntu 22.04安装Python3.10.13

Ubuntu最好设置为英文&#xff0c;我之前用中文在make的test的时候&#xff0c;总是会有fail。 查了下有人怀疑是language的问题&#xff0c;保险起见都用英文&#xff0c;个人实践也证明改为英文就不报错了。 issue 44031: test_embed and test_tabnanny fails if the curre…

【算法刷题 | 二叉树 02】3.21 二叉树的层序遍历01(5题:二叉树的层序遍历、层序遍历||、右视图、层平均值,以及N叉树的层序遍历)

文章目录 5.二叉树的层序遍历5.1 102_二叉树的层序遍历5.1.1问题5.1.2解法&#xff1a;队列 5.2 107_二叉树的层序遍历||5.2.1问题5.2.2解法&#xff1a;队列 5.3 199_二叉树的右视图5.3.1问题5.3.2解决&#xff1a;队列 5.4 637_二叉树的层平均值5.4.1问题5.4.2解决&#xff1…

Dell戴尔XPS 12 9250二合一笔记本电脑原装出厂Windows10系统包下载

链接&#xff1a;https://pan.baidu.com/s/1rqUEM_q5DznF0om6eevcwg?pwdvij0 提取码&#xff1a;vij0 戴尔原厂WIN10系统自带所有驱动、出厂主题壁纸、系统属性专属联机支持标志、系统属性专属LOGO标志、Office办公软件、MyDell等预装程序 文件格式&#xff1a;esd/wim/sw…

API调试管理工具Postman下载及操作介绍

1.下载安装postman地址&#xff1a;https://www.getpostman.com/downloads/ 2.创建项目 3.创建请求API 然后点击save保存api 4.用一个变量保存主域名&#xff0c;方便后续操作 就类似下面的baseurl 5.创建新环境 6.添加变量&#xff08;如添加本地测试环境url——ba…

Qt如何直接处理系统事件(比如鼠标事件),而不是post事件

#include <QtGui/5.15.2/QtGui/qpa/qwindowsysteminterface.h> // 方便调试事件 QWindowSystemInterface::setSynchronousWindowSystemEvents(true); 直接再 qWindowsWndProc函数中处理 通常情况: 事件被放到一个队列中

Leetcode 101. 对称二叉树

心路历程&#xff1a; 这道题没有想象中那么简单。其最难的地方就在于如何判断两个子树相等这件事上&#xff0c;无法直接left right&#xff0c;因为毕竟只是指针。 本道题思考了三种解法&#xff0c;其中一种很可惜没有完全AC。 注意的点&#xff1a; 1、root.left root…

浙江IGM机器人K5控制柜维修需要注意哪些问题?

IGM机器人K5控制柜常见故障及维修方法 1、电源故障&#xff1a; 表现为IGM机器人K5控制柜不能开机或突然断电。 检查&#xff1a;检查电源线是否连接良好&#xff0c;有无破损&#xff1b;检查电源模块的输出电压是否正常&#xff1b; 维修方法&#xff1a;如电源模块损坏&…

【并查集专题】【蓝桥杯备考训练】:网络分析、奶酪、合并集合、连通块中点的数量、格子游戏【已更新完成】

目录 1、网络分析&#xff08;第十一届蓝桥杯省赛第一场C A组/B组&#xff09; 2、奶酪&#xff08;NOIP2017提高组&#xff09; 3、合并集合&#xff08;模板&#xff09; 4、连通块中点的数量&#xff08;模板&#xff09; 5、格子游戏&#xff08;《信息学奥赛一本通》…

DashVector - 阿里云向量检索服务

DashVector 文章目录 DashVector一、关于 DashVector二、使用 DashVector 前提准备1、创建Cluster&#xff1a;2、获得API-KEY3、安装最新版SDK 三、快速使用 DashVector1. 创建Client2. 创建Collection3、插入Doc4、相似性检索5、删除Doc6. 查看Collection统计信息7. 删除Coll…

jeect-boot queryFieldBySql接口RCE漏洞(CVE-2023-4450)复现

jeect-boot积木报表由于未授权的 API /jmreport/queryFieldBySql 使用了 freemarker 解析 SQL 语句从而导致了 RCE 漏洞的产生。 1.漏洞级别 高危 2.漏洞搜索 fofa app"Jeecg-Boot 企业级快速开发平台"3.影响范围 JimuReport < 1.6.14.漏洞复现 这个漏洞的…

(done) 机器学习中的方差 variance 和 偏差 bias 怎么理解?

来源&#xff1a;https://blog.csdn.net/weixin_41479678/article/details/116230631 情况1属于&#xff1a;低 bias&#xff0c;高 variance (和 human performance 相近&#xff0c;但和 验证集dev set 相远) 通常意味着模型训练轮数太多 情况2属于&#xff1a;高 bias&#…

uniapp自定义导航栏左中右内容和图标,以及点击事件

uniapp自定义导航栏左中右内容和图标&#xff0c;以及点击事件 效果&#xff1a; 页面&#xff1a; <view class"navigation-bar"><view class"navigation-bar-left" click"navigateBack"><u-icon name"arrow-left"…

StarRocks-2.5.13部署安装

1、安装jdk11 tar xf jdk-11.0.16.1_linux-x64_bin.tar.gz mv jdk-11.0.16.1 /data/soft/jdk-11 # 配置在/etc/profile中 export JAVA_HOME/data/soft/jdk-11 export CLASSPATH.:/data/soft/jdk-11/lib export PATH/data/soft/jdk-11/bin:$PATH # 验证jdk [rootdb-public-03 s…

就业班 第二阶段 2401--3.19 day4 主从复制

一、MySQL-Replication&#xff08;主从复制&#xff09; 1.1、MySQL Replication 主从复制&#xff08;也称 AB 复制&#xff09;允许将来自一个MySQL数据库服务器&#xff08;主服务器&#xff09;的数据复制到一个或多个MySQL数据库服务器&#xff08;从服务器&#xff09;…

open3d | ubuntu源码编译open3d

# clone源码 git clone https://github.com/isl-org/Open3D# 安装依赖 cd Open3D util/install_deps_ubuntu.sh# 安装anaconda3&#xff0c;略过~ conda create -n open3d_py39 python3.9 conda activate open3d_py39# 查看一下python路径 which pythonmkdir build cd build# c…