16. 一个I/O项目:构建命令行程序(上)

news2024/11/26 12:14:06

标题

  • 一、功能
  • 二 、接受命令行参数
  • 三、 读取文件
  • 四、重构改进模块性和错误处理
    • 4.1 二进制项目的关注分离
    • 4.2 提取参数解析器
    • 4.3 创建一个Config的构造函数
    • 4.4 传参错误处理
    • 4.5 从main中提取逻辑
    • 4.6 将代码拆分进crate

一、功能

  • grep 最简单的使用场景是在特定文件中搜索指定字符串;
  • 程序的运行结果是能在文件中寻找目标字符串;
  • 使用cargo new minigrep创建新项目;

二 、接受命令行参数

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    println!("{:?}",args);
    
    let query = &args[1];
    let filename = &args[2];
    println!("Search for {}", query);
    println!("In file {}", filename);
}
  • std::env::args返回一个传递给程序行参数的迭代器;
  • 调用迭代器的collect方法将会得到一个集合;
  • 集合的第一个值是当前程序的名称,后面的值是传递给程序的参数;

运行结果
在这里插入图片描述

  • 红框标注的就是所有的参数列表;
  • 然后分别输出了queryfilename的值;
  • 程序就是要在文件filename中查找query对应的字符串;

三、 读取文件

  1. 创建文件poem.txt文件,内容如下
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!
  1. 为main.rs增加打开文件的代码
use std::fs;
use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    // println!("{:?}",args);
    
    let query = &args[1];
    let filename = &args[2];
    // println!("Search for {}", query);
    println!("In file {}", filename);

    let contents = fs::read_to_string(filename).expect("Something went wrong reading the file");
    println!("With text:\n{}", contents);
}
  • 引入std::fs,需要用它的read_to_string函数;
  • 注释不需要的打印
  • 使用cargo run needly poem.txt运行程序(目前needly参数没有起作用)

下面的运行结果显示实现了文件读取的功能
在这里插入图片描述

四、重构改进模块性和错误处理

程序目前有四个问题需要修复:

  • 分离解析参数与打开文件功能;
  • 添加配置变量的结构;
  • 添加更多的文件打开失败原因;
  • 添加专用的错误处理函数

4.1 二进制项目的关注分离

  • main函数变得庞大时进程二进制分离的指导步骤:
  1. 将程序拆分成 main.rs 和 lib.rs 并将程序的逻辑放入lib.rs中。
  2. 当命令行解析逻辑比较小时,可以保留在main.rs中。
  3. 当命令行解析开始变得复杂时,也同样将其从 main.rs 提取到lib.rs中。
  • 经过这些处理之后保留在main函数中的函数功能被限制为:
  1. 使用参数值调用命令行解析逻辑;
  2. 设置任何其他的配置;
  3. 调用 lib.rs 中的 run 函数;
  4. 如果 run 返回错误,则处理这个错误;
  • 总之:main.rs处理程序运行,lib.rs处理所有的真正的任务逻辑;

4.2 提取参数解析器

use std::fs;
use std::env;

struct Config{
    query: String,
    filename: String,
}

fn parse_config(args: &[String]) -> Config {
    let query = args[1].clone();
    let filename = args[2].clone();

    Config {query, filename}
}

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

    let contents = fs::read_to_string(config.filename)
                        .expect("Something went wrong reading the file!");

    println!("contents: \n{}", contents);
}

  • 现在的代码还全都在main.rs里边;
  • 将filename和query组合成Config结构体;
  • 结构体中使用完整的、拥有所有权的String类型,因此在parse_config函数中进行clone操作;

4.3 创建一个Config的构造函数

  • 使用之前学过的方法,为结构体构建一个构造函数;
  • 将parse_config的功能移到构建函数里去;
use std::fs;
use std::env;

struct Config{
    query: String,
    filename: String,
}

impl Config{
    fn new(args: &[String]) -> Config{
        let query = args[1].clone();
        let filename = args[2].clone();
        Config{query, filename}
    }
}

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

    let contents = fs::read_to_string(config.filename)
                        .expect("Something went wrong reading the file!");

    println!("contents: \n{}", contents);
}

4.4 传参错误处理

如果new中的参数个数小于3时,会产生错误,添加代码

 fn new(args: &[String]) -> Config{
     assert!(!args.len() < 3, "Not enough arguments");
	 ……
 }
  • assert!宏只有当条件为假时会调用panic!
  • 因此习惯写做”对满足panic!的条件取反";
  • panic!的调用更趋向于程序上的问题而不是使用上的问题;
  • 可以返回一个可以表明成功或错误的Result;

从 new 中返回 Result

  • 返回一个 Result 值,它在成功时会包含一个Config 的实例,错误时会描述问题。
use std::process;

impl Config{
    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();
        Ok(Config {query, filename})
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    
    let config = Config::new(&args).unwrap_or_else(|err| {
        println!("Problem parsing arguments: {}", err);
        process::exit(1);
    });

    let contents = fs::read_to_string(config.filename)
                        .expect("Something went wrong reading the file!");

    println!("contents: \n{}", contents);
}
  • new函数返回一个Result,在成功时带有一个Config实例失败时带有一个&'static str
  • &'static str就代表字符串字面量;
  • unwrap_or_else定义于Result<T, E> 上,使用它可以进行一些自定义的非 panic! 的错误处理;
  • 当Result 是Ok时,它返回 Ok 内部封装的值;
  • 当Result是Err时,该方法会调用一个闭包(closure),也就是一个我们定义的作为参数传递给 unwrap_or_else的匿名函数;

4.5 从main中提取逻辑

  • 创建run 的函数来存放目前main函数中不属于设置配置或处理错误的所有逻辑;
fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let contents = fs::read_to_string(config.filename)?;

    println!("With text:\n{}", contents);

    Ok(())
}

fn main() {
    let args: Vec<String> = env::args().collect();
    
    let config = Config::new(&args).unwrap_or_else(|err| {
        println!("Problem parsing arguments: {}", err);
        process::exit(1);
    });

    if let Err(e) = run(config) {
        println!("Application error: {}", e);

        process::exit(1);
    }
}
  • run函数获取一个Config实例作为参数;
  • run函数的返回类型设置为Result<(), Box<dyn Error>>
  • run函数Ok时返回unit 类型 (),失败时返回了trait对象Box<dyn Error>,这无需指定具体将会返回的值的类型,采用动态识别;
  • run函数里的read_to_string后取消了expect而采用?运算符,这会从函数中返回错误值并由调用者来处理;
  • run成功时返回一个将unit类型值包装的Ok值,并没有什么实际意义;
  • main函数中使用if let检查run返回的是否是一个Err;

4.6 将代码拆分进crate

将main.rs中的下述功能都移动到src/lib.rs中

  • run 函数定义
  • 相关的 use 语句
  • Config 的定义
  • Config::new 函数定义
    src/lib.rs
use std::fs;
use std::error::Error;

pub struct Config{
    query: String,
    filename: String,
}

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();
        
        Ok(Config {query, filename})
    }
}

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

    println!("With text:\n{}", contents);

    Ok(())
}

src/main.rs

use std::env;
use std::process;
use minigrep::Config;

fn main() {
    let args: Vec<String> = env::args().collect();
    
    let config = Config::new(&args).unwrap_or_else(|err| {
        println!("Problem parsing arguments: {}", err);
        process::exit(1);
    });

    if let Err(e) = minigrep::run(config) {
        println!("Application error: {}", e);

        process::exit(1);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1835137.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大模型应用实战2——大模型知识体系、GLM4的function calling功能及agents开发

前面通过glm4作为引子&#xff0c;现在来谈大模型知识体系结构是怎么样的 两个研究方向&#xff1a;开发特定的agents/强化大模型在某个领域的能力&#xff0c;后一个的大致方法如下图 GLM4的function calling功能&#xff1a; 需要解决的问题&#xff1a;不能解决大数运算&a…

香港优才计划是坑人的吗?申请的人都在说……

大家好啊&#xff01;最近看到超多小伙伴私信我说申请香港优才被“坑”了&#xff0c;被割了一波韭菜&#x1f33f;心疼你们&#xff01;作为一个通过这个计划顺利移民香港的老司机&#x1f697;&#xff0c;我必须出来吐槽一下&#xff0c;并且把我的成功经验分享给大家&#…

天锐绿盾 | -公司电脑文件防泄密软件

天锐绿盾是一款专为企业设计的电脑文件防泄密系统&#xff0c;它结合了多种安全功能&#xff0c;旨在从源头上保障企业数据的安全。 www.drhchina.com 以下是关于天锐绿盾的详细介绍&#xff1a; 一、产品概述 天锐绿盾&#xff0c;又名绿盾信息安全管理软件&#xff0c;是一…

【达梦数据库】typeorm+node.js+达梦数据库返回自增列值

1.配置环境&#xff0c;下载依赖包 typeorm init --name test22 --database mysql typeorm-dm&#xff0c;uuid,typeorm2,修改连接信息 修改src/ data-source.ts 文件 连接dm&#xff0c;可参考刚刚安装typeorm-dm 模块中的 README.md 3.修改自增信息 /* 修改前*/PrimaryGen…

FileZilla证书过期,导致FileZilla Client联不上,或者老断开的处理

1、先到服务器上去重新生成一下证书&#xff0c;并且覆盖掉老的证书。edit--settings 输入信息&#xff0c;并且确认生成新的证书&#xff1a; 2、Client连接的时候&#xff0c;弹出证书信任&#xff0c;点击确认。 这样第一次连接&#xff0c;然后访问目录全都是好的&#xff…

解决此平台不支持虚拟化的 Intel VT-x/EPT故障问题

一、问题描述 当我们在VMware Workstation虚拟机上【启用】虚拟系统(如:Windows10)所在的虚【拟机设置】【处理器】【虚拟化引擎】下面的【虚拟化 Intel VT-x/EPT 或AMD-V/RVI(V)】内容后,在启动虚拟系统时,虚拟系统无法启动,且弹出【此平台不支持虚拟化的 Intel VT-x/EP…

算法课程笔记——单调栈单调队列

算法课程笔记——单调栈&单调队列

多协议接入/GB28181/GAT1400协议/安防综合管理系统EasyCVR报错version`GLIBCXX_3.4.19‘not found如何处理?

多协议接入/GB28181/GAT1400协议/安防综合管理系统EasyCVR视频汇聚平台能在复杂的网络环境中&#xff0c;将前端设备统一集中接入与汇聚管理。智慧安防/视频存储/视频监控/视频汇聚EasyCVR平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、…

【自动驾驶】串口通信控制与反馈

文章目录 串口通信控制与反馈通讯协议上行数据帧解析下行数据帧解析串口通信控制与反馈 通讯协议 上行数据指的是机器人底盘向上位机发送的状态数据, 下行数据指的是上位机向机器人底盘发送的控制信息。 上行数据帧解析 机器人运动底盘通过串口发送的数据包格式,如下表所…

练习时长 1 年 2 个月的 Java 菜鸡练习生最近面经,期望25K

面经哥只做互联网社招面试经历分享&#xff0c;关注我&#xff0c;每日推送精选面经&#xff0c;面试前&#xff0c;先找面经哥 自我介绍&#xff1a;本人是练习时长 1 年 2 个月的 Java 后端菜鸡练习生。下面是我最近面试的面经&#xff1a; 百度 一面 约1h时间&#xff1a;2…

MJ绘画设计基础——如何玩转midjourney?

抽卡的时候经常有一个问题&#xff0c;就是整张图都还不错&#xff0c;但是某些地方有些小问题&#xff0c;比如说手很奇怪&#xff0c;比如下面这个图&#xff0c;哪都挺好看&#xff0c;就是左手有点问题。 这时候就可以局部重绘来拯救一下 第一次生成的图 点击图片下方的V…

黄仁勋加州理工毕业典礼演讲:人工智能是我们这个时代最重要的技术

英伟达公司首席执行官黄仁勋周五&#xff08;6月14日&#xff09;在加州理工学院&#xff08;Caltech&#xff09;毕业典礼上发表演讲&#xff0c;鼓励毕业生在逆境中努力&#xff0c;不断寻求新的机遇。 黄说&#xff0c;加州理工学院因其毕业生受人尊敬而闻名&#xff0c;如…

5G+北斗高精度定位终端技术,赋能千行百业应用

5G北斗高精度定位终端技术的融合&#xff0c;正以前所未有的精准度和实时性&#xff0c;为千行百业带来革命性的变革。从智慧城市的精细化管理&#xff0c;到智能交通的实时调度&#xff0c;再到精准农业的播种与收割&#xff0c;这一技术正不断拓宽其应用领域&#xff0c;为各…

驰为电脑软件卸载指南:轻松解决软件占用问题

驰为电脑作为一款性能出色的设备&#xff0c;用户在使用过程中难免会安装各种软件以满足不同的需求。然而&#xff0c;随着时间的推移&#xff0c;一些软件可能变得不再需要&#xff0c;或者我们可能需要更新到新版本。这时&#xff0c;如何有效地卸载不再使用的软件&#xff0…

【启明智显产品分享】Model4 工业级HMI芯片详解(三):高安全、防抄板

Model4 工业级HMI芯片详解系列专题&#xff08;三&#xff09;【高安全、防抄板】 随着物联网和智能设备的快速发展&#xff0c;设备安全认证的需求日益迫切。硬件安全认证和保护在确保设备和身份安全中发挥着不可替代的作用&#xff0c;需要与软件安全相结合&#xff0c;共同构…

【1】、var、let、const 三者的区别

主要围绕一下五个方面 变量提升暂时性死区块级作用域重复声明修改声明的变量 1、变量的提升 【var】可以在声明前使用&#xff0c;即输出为undefined 【let】和【const】未声明不可使用&#xff0c;否则会报错 2、暂时性死区定义&#xff1a;在代码块内&#xff0c;如果引用…

java之mybatis笔记

1 项目创建 1.1 maven设置 1.2 创建项目文件 1.3 配置MyBatis的相关依赖 1.4 配置 MyBatis 创建一个 mybatis-config.xml 配置文件&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org…

翻转数位00

题目链接 翻转数位 题目描述 注意点 可以将一个数位从0变为1找出能够获得的最长的一串1的长度&#xff08;必须是连续的&#xff09; 解答思路 参照题解使用动态规划解决本题&#xff0c;对于任意一个位置i&#xff0c;dp[i][0]表示到达且包含第i位不翻转0最长1的长度&…

Vue3 状态管理 - Pinia,超详细讲解!

前言&#xff1a; 哈喽&#xff0c;大家好&#xff0c;我是前端菜鸟的自我修养&#xff01;今天给大家分享【Vue3 状态管理 - Pinia】&#xff0c;超详细讲解&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;原创不易&#xff0c;如果能帮助到带大…

AxProtector CTP荣膺2024年度德国创新奖,见证软件安全领域新突破

AxProtector CTP通过将混淆技术直接集成到编译过程中&#xff0c;引入了一种突破性的软件安全保护方法&#xff0c;有效对抗逆向工程。适用于Windows、Linux和macOS&#xff0c;确保在各种操作系统上的广泛应用。结合了强大的知识产权保护和灵活的许可功能&#xff0c;包括模块…