zoukankan      html  css  js  c++  java
  • [易学易懂系列|rustlang语言|零基础|快速入门|(25)|实战2:命令行工具minigrep(2)]

    [易学易懂系列|rustlang语言|零基础|快速入门|(25)|实战2:命令行工具minigrep(2)]

    项目实战

    实战2:命令行工具minigrep

    我们继续开发我们的minigrep。

    我们现在以TDD测试驱动开发的模式,来开发新的功能search函数。

    开始吧,我们先在src/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));
        }
    }
    
    

    然后同样再写一个空函数:

    pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
        vec![]
    }
    

    然后,我们要跑一下命令:

    cargo test
    

    结果显示:

    $ cargo test
       Compiling minigrep v0.1.0 (file:///projects/minigrep)
    --warnings--
        Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs
         Running target/debug/deps/minigrep-abcabcabc
    
    running 1 test
    test tests::one_result ... FAILED
    
    failures:
    
    ---- tests::one_result stdout ----
            thread 'tests::one_result' panicked at 'assertion failed: `(left ==
    right)`
    left: `["safe, fast, productive."]`,
    right: `[]`)', src/lib.rs:48:8
    note: Run with `RUST_BACKTRACE=1` for a backtrace.
    
    
    failures:
        tests::one_result
    
    test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
    
    error: test failed, to rerun pass '--lib'
    

    测试不通过。

    我们的实现函数search,只简单返回一个空vector。当然,不通过。

    怎么办?

    重构一下search函数:

    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
    }
    

    然后,我们跑一下命令:

    cargo test
    

    结果显示:

    running 1 test
    test tests::one_result ... ok
    
    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
    

    测试通过!漂亮!

    这就是TDD的基本流程。

    我们看看示图:

    See the source image

    好吧,现在我们可以直接调用search函数了,把它放到src/lib.rs中的run函数里:

    //重构从文件中读取内容的业务逻辑
    pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
        let contents = fs::read_to_string(config.filename)?;
    
        println!("With text:
    {}", contents);
    
        for line in search(&config.query, &contents) {
           println!("-----search result ------{}", line);
        }
        Ok(())
    }
    

    然后,我们用运行命令:

    cargo run frog poem.txt
    

    结果为:

    .......
    -----search result ------How public, like a frog
    

    我们找到包含frog单词的那一行!完美!

    我们再找下body单词:

    cargo run body poem.txt
    

    结果为:

    ......
    -----search result ------I'm nobody! Who are you?
    -----search result ------Are you nobody, too?
    -----search result ------How dreary to be somebody!
    
    

    结果正确!

    当然,我们也可以试试不存在的单词:

    cargo run monomorphization poem.txt
    

    结果为:

    E:code
    ustProjectminigrep> cargo run monomorphization poem.txt
        Finished dev [unoptimized + debuginfo] target(s) in 0.01s
         Running `targetdebugminigrep.exe monomorphization poem.txt`
    With text:
    I'm nobody! Who are you?
    Are you nobody, too?
    Then there's a pair of us - don't tell!
    They'd banish us, you know.
    
    How dreary to be somebody!
    How public, like a frog
    To tell your name the livelong day
    To an admiring bog!
    

    没有出现带“-----search result ------”的结果信息,说明没有找到单词:monomorphization。

    结果符合期望。

    现在我们再以TDD的开发方式来开发新的功能函数search_case_insensitive,新增测试代码:

    #[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函数,让它fail,因为这个逻辑比较简单,那就直接实现吧:

    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
    }
    

    好的,我们跑一下命令:

    cargo test
    

    结果:

    $cargo test
       Compiling minigrep v0.1.0 (E:code
    ustProjectminigrep)
        Finished dev [unoptimized + debuginfo] target(s) in 1.38s
         Running targetdebugdepsminigrep-07da7ef1bffd9ef9.exe
    
    running 2 tests
    test case_insensitive ... ok
    test tests::one_result ... ok
    

    测试通过,漂亮!

    我们现在把这个方法整合到run函数里:

    //重构从文件中读取内容的业务逻辑
    pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
        let contents = fs::read_to_string(config.filename)?;
    
        println!("With text:
    {}", contents);
    
        let results = if config.case_sensitive {
            search(&config.query, &contents)
        } else {
            search_case_insensitive(&config.query, &contents)
        };
        for line in results {
            println!("-----search result ------{}", line);
        }
        Ok(())
    }
    

    当然,我们也要把属性case_sensitive放到结构体Config里面:

    //结构体Config用来封装参数属性
    pub struct Config {
        pub query: String,
        pub filename: String,
        pub case_sensitive: bool,
    }
    
    

    src/lib.rs的完整代码如下 :

    use std::env;
    use std::error::Error;
    use std::fs;
    //结构体Config用来封装参数属性
    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 = 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)?;
    
        println!("With text:
    {}", contents);
    
        let results = if config.case_sensitive {
            search(&config.query, &contents)
        } else {
            search_case_insensitive(&config.query, &contents)
        };
        for line in results {
            println!("-----search result ------{}", 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 query = query.to_lowercase();
        let mut results = Vec::new();
    
        for line in contents.lines() {
            if line.to_lowercase().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));
        }
    }
    #[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)
        );
    }
    
    

    我们现在运行命令:

    cargo run to poem.txt
    

    结果:

    ......
    -----search result ------Are you nobody, too?
    -----search result ------How dreary to be somebody!
    
    

    现在我们把环境变量设置一下:

    如果你的终端是powershell,用以下命令

     $env:CASE_INSENSITIVE=1
    

    我们现在运行命令:

    cargo run to poem.txt
    

    结果:

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

    我们现在看看把错误信息输出到一个output.txt文件,运行以下命令:

    cargo run > output.txt
    

    我们观察到,控制台没有输出。

    但当前工程目录有一个output.txt文件,打开文件。里面有错误信息。

    我们现在来用eprintln!宏替换掉println!宏。src/main.rs代码更新如下:

    use minigrep::run;
    use minigrep::Config;
    
    use std::env;
    use std::process;
    //主函数,程序入口
    fn main() {
        let args: Vec<String> = env::args().collect();
    
        
        let config = Config::new(&args).unwrap_or_else(|err| {
            eprintln!("Problem parsing arguments: {}", err);
            process::exit(1);
        });
    
        if let Err(e) = run(config) {
            //根据处理结果返回值 来处理,如果有错误,则打印信息,并直接退出当前程序
            eprintln!("Application error: {}", e);
    
            process::exit(1);
        }
    }
    
    

    运行以下命令:

    cargo run > output.txt
    

    我们观察到,控制台现在会输出错误信息。

    我们运行如下命令:

     cargo run to poem.txt > output.txt
    

    这里控制台没有输出。

    但打开output.txt,里面的信息为:

    With text:
    I'm nobody! Who are you?
    Are you nobody, too?
    Then there's a pair of us - don't tell!
    They'd banish us, you know.
    
    How dreary to be somebody!
    How public, like a frog
    To tell your name the livelong day
    To an admiring bog!
    -----search result ------Are you nobody, too?
    -----search result ------How dreary to be somebody!
    -----search result ------To tell your name the livelong day
    -----search result ------To an admiring bog!
    
    

    以上,希望对你有用。

    如果遇到什么问题,欢迎加入:rust新手群,在这里我可以提供一些简单的帮助,加微信:360369487,注明:博客园+rust
    

    参考文章:

    https://doc.rust-lang.org/stable/book/ch12-05-working-with-environment-variables.html

  • 相关阅读:
    JVM内存模型与类加载机制
    JS 实现动态轮播图
    Jedis & spring-data-redis
    JAVA反射机制与动态代理
    JavaScript -- 筑基
    IO流与装饰者模式
    ES&IK环境搭建
    Elasticsearch笔记
    DQL
    DDL--DML
  • 原文地址:https://www.cnblogs.com/gyc567/p/12068718.html
Copyright © 2011-2022 走看看