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));
    }
}
相关推荐
xinzheng新政9 小时前
Javascript 深入学习基础·4
javascript·学习·servlet
苏纪云9 小时前
蓝桥杯考前突击
c++·算法·蓝桥杯
W23035765739 小时前
经典算法详解:最长公共子序列 (LCS) —— 从暴力递归到动态规划完整实现
算法·动态规划·最长子序列
pzx_00110 小时前
【优化器】 随机梯度下降 SGD 详解
人工智能·python·算法
charlie11451419110 小时前
通用GUI编程技术——图形渲染实战(二十九)——Direct2D架构与资源体系:GPU加速2D渲染入门
开发语言·c++·学习·架构·图形渲染·win32
小肝一下10 小时前
每日两道力扣,day8
c++·算法·leetcode·哈希算法·hot100
CheerWWW10 小时前
C++学习笔记——线程、计时器、多维数组、排序
c++·笔记·学习
无限进步_10 小时前
【C++】验证回文字符串:高效算法详解与优化
java·开发语言·c++·git·算法·github·visual studio
克里斯蒂亚诺·罗纳尔达10 小时前
智能体学习16——学习与适应(Learning-and-Adaptation)-深入解读
深度学习·学习·机器学习
Meme Buoy10 小时前
18.补充数学1:生成树-最短路径-最大流量-线性规划
数据结构·算法