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));
    }
}
相关推荐
Liu6288818 小时前
C++中的工厂模式高级应用
开发语言·c++·算法
AI科技星19 小时前
全尺度角速度统一:基于 v ≡ c 的纯推导与验证
c语言·开发语言·人工智能·opencv·算法·机器学习·数据挖掘
sinat_2554878119 小时前
读者、作家 Java集合学习笔记
java·笔记·学习
条tiao条19 小时前
KMP 算法详解:告别暴力匹配,让字符串匹配 “永不回头”
开发语言·算法
干啥啥不行,秃头第一名19 小时前
C++20概念(Concepts)入门指南
开发语言·c++·算法
zzh9407720 小时前
Gemini 3.1 Pro 硬核推理优化剖析:思维织锦、动态计算与国内实测
算法
2301_8073671920 小时前
C++中的解释器模式变体
开发语言·c++·算法
愣头不青20 小时前
617.合并二叉树
java·算法
Edward1111111120 小时前
3.18异常学习
学习
MIUMIUKK21 小时前
双指针三大例题
算法