绿色在线网站模板下载工具,河南网站设计,需求登记网站怎么做,全国网站设计公司12.6.0. 写在正文之前
第12章要做一个实例的项目——一个命令行程序。这个程序是一个grep(Global Regular Expression Print)#xff0c;是一个全局正则搜索和输出的工具。它的功能是在指定的文件中搜索出指定的文字。
这个项目分为这么几步#xff1a;
接收命令行参数读取…12.6.0. 写在正文之前
第12章要做一个实例的项目——一个命令行程序。这个程序是一个grep(Global Regular Expression Print)是一个全局正则搜索和输出的工具。它的功能是在指定的文件中搜索出指定的文字。
这个项目分为这么几步
接收命令行参数读取文件重构改进模块和错误处理使用TDD测试驱动开发开发库功能本文使用环境变量将错误信息写入标准错误而不是标准输出
喜欢的话别忘了点赞、收藏加关注哦加关注即可阅读全文对接下来的教程有兴趣的可以关注专栏。谢谢喵(ω)
12.6.1. 回顾
以下是截止到上一篇文章为止所写出的全部代码。
lib.rs:
use std::error::Error;
use std::fs; pub struct Config { pub query: String, pub filename: String,
} impl Config { pub fn new(args: [String]) - ResultConfig, 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(), Boxdyn Error { let contents fs::read_to_string(config.filename)?; println!(With text:\n{}, contents); Ok(())
}main.rs:
use std::env;
use std::process;
use minigrep::Config; fn main() { let args:VecString 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); }
}在前几节中我们完成了对业务逻辑的迁移把它分离到lib.rs里。这样对编写测试帮助很大因为lib.rs中的逻辑不需要在命令行下运行就可以直接使用不同的参数调用业务功能函数并校验其返回值也就是针对业务逻辑进行测试。
12.6.2. 什么是测试驱动开发TDD
TDD是Test-Driven Development的缩写中文名为测试驱动开发一般遵循以下步骤
编写一个会失败的测试运行该测试并确保它是按照预期的原因失败编写或修改刚好足够的代码让新测试通过重构刚刚添加或修改的代码确保测试会通过返回步骤1继续
TDD只是众多软件开发方法中的一种但是它能对代码的设计工作起到指导和帮助的作用。先编写测试然后再编写能够通过测试的代码也有助于开发过程中保持较高的测试覆盖率。
本篇文章会通过测试驱动开发的步骤完成程序的搜索逻辑——在文件内容中搜索指定的字符串将符合的内容的行数放在一个列表中。这个函数会被命名为search。
12.6.3. 修改代码
按照TDD的步骤来写代码
1. 编写会失败的测试
首先到lib.rs里编写一个测试模块
#[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)); }
}也就是说因为query存储的duct在safe, fast, productive.“这一行所以返回值会是元素为String的Vector并且只有一个元素内容会是safe, fast, productive.”
返回值是Vector是因为search函数预期能处理多个符合的结果当然这个测试函数只可能有一个结果这个测试函数取名叫one_result也是因为如此。
写好了测试模块接下来写search函数
pub fn searcha(query: str, contents: a str) - Veca str { vec![]
}为了让这个函数能被外部函数调用得使用pub来声明为公共的这个函数得加生命周期标志因为有多个非self的参数Rust无法判断哪个参数的生命周期跟返回值的生命周期相同。返回值Vector内的元素是字符串切片是从contents截取的所以返回值应和contents的生命周期相同所以给它们两个标注了一样的生命周期a而query则不需要生命周期标注。函数内容只需要确保能通过编译即可因为TDD的第一步是编写一个会出错的测试所以出错才是想要的结果。
测试结果
$ cargo testCompiling minigrep v0.1.0 (file:///projects/minigrep)Finished test profile [unoptimized debuginfo] target(s) in 0.97sRunning unittests src/lib.rs (target/debug/deps/minigrep-9cd200e5fac0fc94)running 1 test
test tests::one_result ... FAILEDfailures:---- tests::one_result stdout ----
thread tests::one_result panicked at src/lib.rs:44:9:
assertion left right failedleft: [safe, fast, productive.]right: []
note: run with RUST_BACKTRACE1 environment variable to display a backtracefailures:tests::one_resulttest result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00serror: test failed, to rerun pass --lib这个测试失败了但没问题这就是TDD第一步想要的结果。
2. 编写或修改刚好足够的代码让新测试通过
第一步完成接下来执行TDD的第二步编写或修改刚好足够的代码让新测试通过。
思考search的思路应该是遍历contents的每一行在遍历的时候查找是否有符合query的字符串有就把这一行放到返回值的列表中如果没有什么都不做遍历下一行。最后把所有结果放到Vector里返回即可。 对于遍历每一行可以使用lines方法它会返回一个迭代器13章会细讲会把字符串的内容一行一行地返回。 对于查找是否有符合query的字符串可以使用contains方法它返回的是一个布尔类型有符合的就返回true反之则为false。 最后别忘了要把符合的行放到Vector里。
根据以上这些知识就可以写出代码了
pub fn searcha(query: str, contents: a str) - Veca str { let mut results Vec::new(); for line in contents.lines() { if line.contains(query) { results.push(line); } } results
}注意这里results不用显示声明元素类型是因为下文中往这个Vector里添加了line这个str类型Rust推断出results里的元素类型是str。
现在运行一下测试
$ cargo testCompiling minigrep v0.1.0 (file:///projects/minigrep)Finished test profile [unoptimized debuginfo] target(s) in 1.22sRunning unittests src/lib.rs (target/debug/deps/minigrep-9cd200e5fac0fc94)running 1 test
test tests::one_result ... oktest result: ok. 1 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函数中使用search函数
search函数目前写好了那就可以在run函数中调用了
pub fn run(config: Config) - Result(), Boxdyn Error { let contents fs::read_to_string(config.filename)?; for line in search(config.query, contents) { println!({}, line); } Ok(())
}通过循环的方式找到符合的一行就立马打印出来。
试运行一下
$ cargo run -- frog poem.txtCompiling minigrep v0.1.0 (file:///projects/minigrep)Finished dev profile [unoptimized debuginfo] target(s) in 0.38sRunning target/debug/minigrep frog poem.txt
How public, like a frog这个例子只有单行试试有多行的字符
$ cargo run -- body poem.txtCompiling minigrep v0.1.0 (file:///projects/minigrep)Finished dev profile [unoptimized debuginfo] target(s) in 0.0sRunning target/debug/minigrep body poem.txt
Im nobody! Who are you?
Are you nobody, too?
How dreary to be somebody!试一个没有的词汇
$ cargo run -- monomorphization poem.txtCompiling minigrep v0.1.0 (file:///projects/minigrep)Finished dev profile [unoptimized debuginfo] target(s) in 0.0sRunning target/debug/minigrep monomorphization poem.txt