Rust学习记录--C12 实例:写一个命令行程序

C12 实例:写一个命令行程序

  • [1. 读取命令行参数](#1. 读取命令行参数)
  • [2. 读取文件](#2. 读取文件)
  • [3. 处理错误](#3. 处理错误)
    • [3.1 处理传入参数可能导致的错误](#3.1 处理传入参数可能导致的错误)
    • [3.2 处理一些标准错误,类似于文件读取错误等等](#3.2 处理一些标准错误,类似于文件读取错误等等)
    • [3.3 在调用函数时如何处理潜在的exception](#3.3 在调用函数时如何处理潜在的exception)
  • [4. 重构->增进模块](#4. 重构->增进模块)
  • [5. 使用测试驱动开发](#5. 使用测试驱动开发)
  • [6. 错误处理代码集中放置,方便后期修改](#6. 错误处理代码集中放置,方便后期修改)
  • [7. 具体实现](#7. 具体实现)

要求:在一个文件中搜索字符串返回对应的行的列表

1. 读取命令行参数

use std::env::args() // 并且使用collect() 转换为Vec<String>

rust 复制代码
let args : Vec<String> = env::args().collect();

2. 读取文件

use std::fs::read_to_string()

rust 复制代码
let file_content = fs::read_to_string(config.filename)?;

3. 处理错误

3.1 处理传入参数可能导致的错误

使用

rust 复制代码
Result<Configuration, &'static str>

例子:

rust 复制代码
pub fn new(args: &[String]) -> Result<Configuration, &'static str>{
    if args.len() < 3 {
        return Err("The input args should be 2")
    }

    let query = args[1].clone();
    let filename = args[2].clone();
    Ok(Configuration { query, filename })
}

3.2 处理一些标准错误,类似于文件读取错误等等

在表达式结尾加上?

返回类型:

rust 复制代码
Result<(), Box<dyn Error>>

例子:

rust 复制代码
pub fn run(config: Configuration) -> Result<(), Box<dyn Error>> {
   
    let file_content = fs::read_to_string(config.filename)?;

    for item in search(&config.query, &file_content)
    {
        println!("The query: '{}' in : {}", config.query, item);
    }
    Ok(())
}

3.3 在调用函数时如何处理潜在的exception

针对返回结果是Result<T,E>的类型,使用if let

rust 复制代码
    if let Err(e) = App::run(config) {
        println!("Application error: {}",e);
        process::exit(1);
    }

4. 重构->增进模块

  • 配置变量放在一个结构体中
    • 搞出一个结构体,并设置new函数
  • 每个函数只负责一个功能

5. 使用测试驱动开发

  • 通过写测试的方式实现开发

  • 逻辑

    • 编写一个会失败的测试,运行该测试,确保它是按照预期的原因失败(在此期间,确认调用的函数输入和输出)
    • 编写或修改刚好足够的代码,让测试通过
    • 重构刚刚添加或修改的代码,确保测试会始终测试通过
    • 返回步骤1,继续
  • 新的开发:实现区分大小写和不区分大小写的两种search,并且将环境变量作为该功能的开关

    • 来自环境变量:use std::env::var("环境变量的名称")
    rust 复制代码
    // 只要出现CASE_INSENSITIVE,就认为是不区分大小写的,不出现就表示区分大小写
    // env::var("CASE_INSENSITIVE")的返回结果是Result
    let case_sensitive = env::var("CASE_INSENSITIVE").is_err();

6. 错误处理代码集中放置,方便后期修改

  • 区分标准错误和标准输出
    • 标准输出:stdout
      • println!
    • 标准错误:stderr
      • eprintln!
  • cargo run > output.txt

7. 具体实现

rust 复制代码
// main.rs
use std::{env, process};

use App::Configuration;

fn main() {    
    let args : Vec<String> = env::args().collect();

    let config = Configuration::new(&args).unwrap_or_else(|err| {
        eprintln!("Problem pasing arg: {}", err);
        process::exit(-1);
    });

    if let Err(e) = App::run(config) {
        eprintln!("Application error: {}", e);
        process::exit(-1);
    }
}
rust 复制代码
// lib.rs
use std::error::Error;
use std::fs;
use std::env;

pub struct Configuration {
    pub query: String,
    pub filename: String,
    pub case_sensitive: bool,
}

impl Configuration {
    pub fn new(args: &[String]) -> Result<Configuration, &'static str>{
        if args.len() < 3 {
            return Err("The input args should be 2")
        }

        let query = args[1].clone();
        let filename = args[2].clone();
        let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
        Ok(Configuration { query, filename, case_sensitive })
    }
}

pub fn run(config: Configuration) -> Result<(), Box<dyn Error>> {
    
    let file_content = fs::read_to_string(config.filename)?;

    let res = if config.case_sensitive {
        search_sensitive(&config.query, &file_content)
    } else {
        search_insensitive(&config.query, &file_content)        
    };

    for item in res
    {
        eprintln!("The query: '{}' in : {}", config.query, item);
    }
    Ok(())
}

pub fn search_insensitive<'a>(query: &str, content: &'a str) -> Vec<&'a str> {
    let mut vec = Vec::new();
    for item in content.lines() {
        if item.to_lowercase().contains(&query.to_lowercase())
        {
            vec.push(item);
        }
    }
    vec
}


pub fn search_sensitive<'a>(query: &str, content: &'a str) -> Vec<&'a str> {
    let mut vec = Vec::new();
    for item in content.lines() {
        if item.contains(query)
        {
            vec.push(item);
        }
    }
    vec
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn one_result_sensitive() {
        let query = "streeT";
        let content = "\
        The morning sun painted the sky with soft orange light.
Birds sang quietly while the city slowly came awake.
A gentle breeze moved leaves along the empty streeT.
Coffee shops opened doors, releasing warm familiar street.";
        assert_eq!(
            vec!["A gentle breeze moved leaves along the empty streeT."], 
            search_sensitive(query, content));
    }

    #[test]
    fn one_result_insensitive() {
        let query = "streeT";
        let content = "\
        The morning sun painted the sky with soft orange light.
Birds sang quietly while the city slowly came awake.
A gentle breeze moved leaves along the empty streeT.
Coffee shops opened doors, releasing warm familiar street.";
        assert_eq!(
            vec!["A gentle breeze moved leaves along the empty streeT.", "Coffee shops opened doors, releasing warm familiar street."], 
            search_insensitive(query, content));
    }
}
相关推荐
阿蒙Amon6 小时前
TypeScript学习-第10章:模块与命名空间
学习·ubuntu·typescript
AI绘画哇哒哒6 小时前
【干货收藏】深度解析AI Agent框架:设计原理+主流选型+项目实操,一站式学习指南
人工智能·学习·ai·程序员·大模型·产品经理·转行
那个村的李富贵6 小时前
CANN加速下的AIGC“即时翻译”:AI语音克隆与实时变声实战
人工智能·算法·aigc·cann
power 雀儿6 小时前
Scaled Dot-Product Attention 分数计算 C++
算法
琹箐7 小时前
最大堆和最小堆 实现思路
java·开发语言·算法
戌中横7 小时前
JavaScript——预解析
前端·javascript·学习
renhongxia17 小时前
如何基于知识图谱进行故障原因、事故原因推理,需要用到哪些算法
人工智能·深度学习·算法·机器学习·自然语言处理·transformer·知识图谱
坚持就完事了7 小时前
数据结构之树(Java实现)
java·算法
算法备案代理8 小时前
大模型备案与算法备案,企业该如何选择?
人工智能·算法·大模型·算法备案
●VON8 小时前
React Native for OpenHarmony:2048 小游戏的开发与跨平台适配实践
javascript·学习·react native·react.js·von