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));
    }
}
相关推荐
NAGNIP7 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
颜酱15 小时前
单调栈:从模板到实战
javascript·后端·算法
CoovallyAIHub19 小时前
仿生学突破:SILD模型如何让无人机在电力线迷宫中发现“隐形威胁”
深度学习·算法·计算机视觉
CoovallyAIHub19 小时前
从春晚机器人到零样本革命:YOLO26-Pose姿态估计实战指南
深度学习·算法·计算机视觉
CoovallyAIHub19 小时前
Le-DETR:省80%预训练数据,这个实时检测Transformer刷新SOTA|Georgia Tech & 北交大
深度学习·算法·计算机视觉
CoovallyAIHub19 小时前
强化学习凭什么比监督学习更聪明?RL的“聪明”并非来自算法,而是因为它学会了“挑食”
深度学习·算法·计算机视觉
CoovallyAIHub19 小时前
YOLO-IOD深度解析:打破实时增量目标检测的三重知识冲突
深度学习·算法·计算机视觉
NAGNIP1 天前
轻松搞懂全连接神经网络结构!
人工智能·算法·面试
NAGNIP1 天前
一文搞懂激活函数!
算法·面试
董董灿是个攻城狮1 天前
AI 视觉连载7:传统 CV 之高斯滤波实战
算法