【Rust自学】12.7. 使用环境变量

ops/2025/1/16 14:56:23/

12.7.0. 写在正文之前

第12章要做一个实例的项目——一个命令行程序。这个程序是一个grep(Global Regular Expression Print),是一个全局正则搜索和输出的工具。它的功能是在指定的文件中搜索出指定的文字。

这个项目分为这么几步:

  • 接收命令行参数
  • 读取文件
  • 重构:改进模块和错误处理
  • 使用TDD(测试驱动开发)开发库功能
  • 使用环境变量(本文)
  • 将错误信息写入标准错误而不是标准输出

喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
请添加图片描述

12.7.1. 回顾

以下是截止到上一篇文章为止所写出的全部代码。

lib.rs:

rust">use std::error::Error;  
use std::fs;  pub struct Config {  pub query: String,  pub filename: String,  
}  impl Config {  pub fn new(args: &[String]) -> Result<Config, &'static str> {  if args.len() < 3 {  return Err("Not enough arguments");  }  let query = args[1].clone();  let filename = args[2].clone();  Ok(Config { query, filename})  }  
}  pub fn run(config: Config) -> Result<(), Box<dyn Error>> {  let contents = fs::read_to_string(config.filename)?;  for line in search(&config.query, &contents) {  println!("{}", line);  }  Ok(())  
}  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  
}  #[cfg(test)]  
mod tests {  use super::*;  #[test]  fn one_result() {  let query = "duct";  let contents = "\  
Rust:  
safe, fast, productive.  
Pick three.";  assert_eq!(vec!["safe, fast, productive."],search(query, contents));  }  
}

main.rs:

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

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

12.7.2. 编写不区分大小写的search函数

这里不区分大小写的功能是通过环境变量来实现的,当然也可使用参数来实现,但使用环境变量的好处是只需要配置一次就可以在整个终端的会话中一直保持有效。

对于这个功能,我们也使用TDD(测试驱动开发)流程来开发:

  • 编写一个会失败的测试,运行该测试,并确保它是按照预期的原因失败
  • 编写或修改刚好足够的代码,让新测试通过
  • 重构刚刚添加或修改的代码,确保测试会通过
  • 返回步骤1,继续

1. 编写一个会失败的测试

这个对大小写不敏感的函数先给它起个名叫做search_case_insensitive

先把测试模块改一下,改出一个对大小写敏感的测试函数和不敏感的测试函数:

rust">#[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));}
}

然后再写search_case_insensitive函数的具体内容:

rust">pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {  vec![]
}
  • 为了让这个函数能被外部函数调用,得使用pub来声明为公共的
  • 这个函数得加生命周期标志,因为有多个非self的参数,Rust无法判断哪个参数的生命周期跟返回值的生命周期相同。
  • 返回值Vector内的元素是字符串切片,是从contents截取的,所以返回值应和contents的生命周期相同,所以给它们两个标注了一样的生命周期'a,而query则不需要生命周期标注。
  • 函数内容只需要确保能通过编译即可,因为TDD的第一步是编写一个会出错的测试,所以出错才是想要的结果。

这时候跑测试肯定会失败,但没关系,这正是TDD第一步想要的

2. 编写或修改刚好足够的代码,让新测试通过

其实search_case_insensitive的代码与search的大部分都差不多,只需要做一些小修改即可。逻辑很好想,就是把关键词和文本内容都变成全小写即可:

rust">pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {  let mut results = Vec::new();  let query = query.to_lowercase();  for line in contents.to_lowercase().lines() {  if line.contains(&query) {  results.push(line);  }  }  results  
}
  • to_lowercase方法可以把字符串变成全小写
  • to_lowercase转换后的结果是String,变量拥有所有权,也就是新的queryString而不是&str。在循环中的if语句使用的是&query,因为contains方法不接受String所以得传引用进去。

再跑一下测试:

$ cargo testCompiling minigrep v0.1.0 (file:///projects/minigrep)Finished `test` profile [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

两个测试都通过了

3. 在run函数中使用此函数

这个函数没问题了,就可以在run函数中调用了。

但是首先得先为Config结构体添加一个字段来作为使用普通的search还是对大小写不敏感的search_case_insensitive的依据:

rust">pub struct Config {  pub query: String,  pub filename: String,  pub case_sensitive: bool,  
}

修改run函数让它判断配置:

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

Config上的new这个构造器也得改,根据环境变量给case_sensitive这个字段赋值:

rust">impl Config {  pub fn new(args: &[String]) -> Result<Config, &'static str> {  if args.len() < 3 {  return Err("Not enough arguments");  }  let query = args[1].clone();  let filename = args[2].clone();  let case_sensitive = std::env::var("CASE_INSENSITIVE").is_err();  Ok(Config { query, filename, case_sensitive})  }  
}

这里使用了std::env::var这个函数(当然也可以先把std::env导入作用域,再使用env::var),它的参数是这个环境变量的名称(按惯例全大写),这里我写的是CASE_INSENSITIVE,中文翻译过来就是大小写不敏感。这种环境变量只要出现就认为不区分大小写,不出现就认为区分大小写。

std::env::var的返回值是Result类型的,如果这个CASE_INSENSITIVE环境变量被设置了,返回包含环境变量值的Ok(String),反之返回Err(std::env::VarError)

这里std::env::var后面还跟了is_err这个方法,如果is_errErr变体,就会返回赋true给变量case_sensitive,反之就是赋false

PS:说实话,这个小程序写成这个B样也是为了教学的无奈之举,我看到一半我都被这个代码量气笑了,真正写的时候没必要写得这么一板一眼的

12.7.3. 整体代码与试运行

写了这么多,看看截止到目前的所有代码。

lib.rs:

rust">use std::error::Error;  
use std::fs;  pub struct Config {  pub query: String,  pub filename: String,  pub case_sensitive: bool,  
}  impl Config {  pub fn new(args: &[String]) -> Result<Config, &'static str> {  if args.len() < 3 {  return Err("Not enough arguments");  }  let query = args[1].clone();  let filename = args[2].clone();  let case_sensitive = std::env::var("CASE_INSENSITIVE").is_err();  Ok(Config { query, filename, case_sensitive})  }  
}  pub fn run(config: Config) -> Result<(), Box<dyn Error>> {  let contents = fs::read_to_string(config.filename)?;  let results = if config.case_sensitive {  search(&config.query, &contents)  } else {  search_case_insensitive(&config.query, &contents)  };  for line in results {  println!("{}", line);  }  Ok(())  
}  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 mut results = Vec::new();  let query = query.to_lowercase();  for line in contents.to_lowercase().lines() {  if line.contains(&query) {  results.push(line);  }  }  results  
}  #[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)  );  }  
}

main.rs:

rust">use std::env;  
use std::process;  
use minigrep::Config;  fn main() {  let args:Vec<String> = env::args().collect();  let config = Config::new(&args).unwrap_or_else(|err| {  println!("Problem parsing arguments: {}", err);  process::exit(1);  });  if let Err(e) = minigrep::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` profile [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 ,其它不变:

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

会得到:

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

没有任何问题。

注意,如果你在powershell中,设置环境变量得这么写:

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

这会使这个环境变量在这个会话中一致存在,如果要去掉这个环境变量,写:

PS> Remove-Item Env:IGNORE_CASE

http://www.ppmy.cn/ops/150578.html

相关文章

机器学习(1):线性回归概念

1 线性回归基础 1.1 什么是线性 例如&#xff1a;汽车每小时60KM&#xff0c;3小时可以行使多长距离&#xff1f;已知汽车的速度&#xff0c;则汽车的行使距离只与时间唯一相关。在二元的直角坐标系中&#xff0c;描出这一关系的图是一条直线&#xff0c;所以称为线性关系。 线…

炸砖块游戏的最终图案

描述 小红正在玩一个“炸砖块”游戏,游戏的规则如下:初始有一个 n * m 的砖块矩阵。小红会炸 k 次,每次会向一个位置投炸弹,如果这个位置有一个砖块,则砖块消失,上方的砖块向下落。小红希望你画出最终砖块的图案。 输入描述 第一行输入三个正整数 n, m, k,代表矩阵的行…

python创建pdf水印,希望根据文本长度调整水印字体大小,避免超出页面

为了根据文本长度动态调整水印字体大小&#xff0c;可以先测量文本长度&#xff0c;然后根据页面宽度和高度动态计算合适的字体大小。以下是修改后的代码&#xff1a; from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import letter from reportlab.pdfbas…

Unity3D 移动端CPU端性能调优详解

前言 在Unity3D开发中&#xff0c;特别是在移动端&#xff0c;性能优化至关重要。CPU主要负责逻辑运算、物理计算和碰撞检测等核心任务。优化CPU性能不仅能提升游戏的流畅度&#xff0c;还能减少电量消耗和发热问题。本文将详细介绍Unity3D移动端CPU端的性能调优技术&#xff…

DATACOM-防火墙-复习-实验

防火墙 概述配置实验参考 概述 与路由器对比 路由器防火墙功能寻址、流量转发、路由流量控制、安全区域隔离防护硬件-相较于路由器&#xff0c;多了SPU&#xff0c;用于DDoS攻击防范/匹配会话/状态检测/认证策略/安全策略/NAT 策略/内容安全带宽策略等 发展 安全区域 防火墙…

用sklearn运行分类模型,选择AUC最高的模型保存模型权重并绘制AUCROC曲线(以逻辑回归、随机森林、梯度提升、MLP为例)

诸神缄默不语-个人CSDN博文目录 文章目录 1. 导入包2. 初始化分类模型3. 训练、测试模型&#xff0c;绘图&#xff0c;保存指标 1. 导入包 from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier, GradientBoostingClass…

SEO优化与版权保护:提升WordPress网站内容安全和价值

保护网站内容不仅仅是为了防止盗窃&#xff0c;更重要的是确保你的网站能够在搜索引擎中占据有利位置。通过结合SEO优化和版权保护&#xff0c;你可以提升网站的内容安全性&#xff0c;并进一步提升其在搜索引擎中的价值。 1. 版权保护如何影响SEO 版权保护对SEO有直接影响。…

2501,wtl显示html

原文 在MFC程序中有专门封装的CHTMLView来显示超文本文件,如果在对话框中显示网页可用CDHTMLDialog,甚至可实现多页超文本向导风格的对话框,但是在WTL中却没有单独封装超文本的对应控件,这是因为COM组件的使用和编写本来就是ATL的强项,WTL扩展的是ATL欠缺的桌面应用的功能部分…