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

标题

  • 一、功能
  • 二 、接受命令行参数
  • 三、 读取文件
  • 四、重构改进模块性和错误处理
    • 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.mzph.cn/diannao/29569.shtml

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

相关文章

Kubernetes 如何删除 Terminating 状态的 Pod

在 Kubernetes (k8s) 中&#xff0c;Pod 是运行容器化应用的最小部署单元。当我们删除一个 Pod 时&#xff0c;通常它会快速进入 Terminating 状态并被删除。然而&#xff0c;有时由于种种原因&#xff0c;Pod 会长时间停留在 Terminating 状态。本文将详细介绍如何处理和删除这…

深入Node.js:实现网易云音乐数据自动化抓取

随着互联网技术的飞速发展&#xff0c;数据已成为企业和个人获取信息、洞察市场趋势的重要资源。音频数据&#xff0c;尤其是来自流行音乐平台如网易云音乐的数据&#xff0c;因其丰富的用户交互和内容多样性&#xff0c;成为研究用户行为和市场动态的宝贵资料。本文将深入探讨…

C语言 | Leetcode C语言题解之第146题LRU缓存

题目&#xff1a; 题解&#xff1a; typedef struct {int key;int val;UT_hash_handle hh; } LRUCache;LRUCache* cache NULL; int g_capacity 0; LRUCache** lRUCacheCreate(int capacity) {g_capacity capacity;return &cache; }int lRUCacheGet(LRUCache** obj, int…

你对SSH协议了解吗

SSH&#xff08;Secure Shell&#xff09;协议&#xff0c;作为网络通信领域的一项核心技术&#xff0c;以其卓越的安全性能和广泛的应用范围&#xff0c;成为保障网络通信安全的重要工具。本文将深入剖析SSH协议的工作原理、核心特性以及在现代网络通信中的关键作用&#xff0…

一颗B+树可以存储多少数据?

一、前言 这个问题&#xff0c;非常经典&#xff0c;考察的点很多&#xff1a; 比如&#xff1a; 1、操作系统存储的单元&#xff0c;毕竟mysql也是运行在操作系统之上的应用。 2、B树是针对Mysql的InnoDB存储引擎&#xff0c;所以要理解InnoDb的最小存储单元&#xff0c;页&…

业务/吞吐量与存量数据设计关系+压测常见解决方案

前言 1、性能测试中业务量、吞吐量和存量数据的设计关系 1&#xff09;业务量 是不带时间单位。我们提到业务量的时候&#xff0c;一定会加一个时间单位。比如说&#xff0c;每天的业务量是 100 万笔&#xff0c;每年的业务量是 1 亿笔&#xff0c;等等。 2&#xff09;吞吐…

Windows 安装 java 环境

搭建java开发环境 java的产品叫JDK&#xff08;java开发者工具包&#xff09;,必须安装JDK才能使用Java。 一、下载——java下载网址 二、安装 直接全部下一步就行&#xff0c;&#xff08;安装路径可以更换一下&#xff09;。 配置JAVA_HOME环境变量&#xff0c; 安装完成后…

【方法】如何隐藏和保护Excel表格中的敏感数据?

在工作中&#xff0c;很多人经常需要处理包含敏感信息的Excel表格。 为了确保这些数据的安全性&#xff0c;我们可以通过隐藏单元格、行和列&#xff0c;以及设置密码保护工作表的方法&#xff0c;来保护数据&#xff0c;下面一起来看看吧&#xff01; 一、隐藏数据&#xff1…

【免费API推荐】:为您的项目提供高质量的数据和功能(7)

开发者必备的免费实用类的API是现代开发领域中不可或缺的工具。这些API提供了各种功能和服务&#xff0c;能够帮助开发者快速构建强大、高效的应用程序。无论是地理位置服务、人脸识别、支付接口还是社交媒体集成&#xff0c;这些免费API为开发者提供了丰富的功能和数据&#x…

Exposure X7 Mac软件下载-Exposure X7 Mac正式版下载【照片编辑软件】附加详细安装步骤

Exposure X7 Mac正式版是款专门为Mac平台的用户打造的图像编辑工具。Exposure X7 Mac最新版提供了强大的色彩编辑器、自动调整、批量处理、一键预设、遮罩工具、无损层、人像修饰等等功能。并且Exposure X7 Mac还还结合了专业级的照片调整&#xff0c;在配合庞大的华丽照片外观…

谷歌可穿戴设备与生成式AI模型PH-LLM:打造个性化健康监测与指导的新纪元

随着移动和可穿戴设备的普及&#xff0c;它们为个人健康监测提供了前所未有的机会&#xff0c;通过收集步数、心率变异性、睡眠持续时间等连续、精细和纵向数据&#xff0c;帮助用户实时跟踪自己的健康状况。这些数据不仅可以用于简单的监测&#xff0c;还可以结合生成式人工智…

大模型备案:规范管理,确保AI健康发展

随着人工智能技术的飞速发展&#xff0c;大型预训练模型&#xff08;以下简称“大模型”&#xff09;已经在自然语言处理、计算机视觉、语音识别等领域取得了显著成果。这些模型通过在海量数据上进行预训练&#xff0c;能够捕捉到丰富的特征信息&#xff0c;为各种下游任务提供…

语言大模型:开启自然语言处理的新篇章

随着人工智能技术的飞速发展&#xff0c;自然语言处理&#xff08;NLP&#xff09;领域取得了显著的成果。其中&#xff0c;语言大模型&#xff08;Language Models&#xff09;作为近年来崛起的一种新型神经网络模型&#xff0c;已经在文本生成、机器翻译、情感分析等多个NLP任…

NLP自然语言处理课程设计—基于实体识别的智能任务系统

NLP课程设计-基于实体识别的智能任务系统 前言一、数据获取可行性分析和需求分析1. 数据获取可行性分析2. 需求分析 二、程序主要NLP技术2.1 文本分类技术2.2 中文命名实体识别2.2.1 BiLSTM&#xff08;双向长短期记忆网络&#xff09;2.2.2 CRF&#xff08;条件随机场&#xf…

计算机专业毕设-在线商城系统

1 项目介绍 在线商城系统&#xff0c;后端java语言&#xff0c;springboot&#xff0c;SSM框架。前端thymeleaf&#xff0c;前后端不分离。本项目已经隐去作者信息&#xff0c;所有代码文件均没有创建人和创建时间&#xff0c;可以放心使用。 系统用户分为两类&#xff0c;管理…

【毕业设计】Django 校园二手交易平台(有源码+mysql数据)

此项目有完整实现源码&#xff0c;有需要请联系博主 Django 校园二手交易平台开发项目 项目选择动机 本项目旨在开发一个基于Django的校园二手交易平台&#xff0c;为大学生提供一个安全便捷的二手物品买卖平台。该平台将提供用户注册和认证、物品发布和搜索、交易信息管理等…

嵌入式实训day6

1、 from machine import Pin from neopixel import NeoPixel import timeif __name__"__main__"#创建RBG灯带控制对象&#xff0c;包含5个像素(5个RGB LED)rgb_led NeoPixel(Pin(4,Pin.OUT)&#xff0c;5)#定义RGB颜色RED(255&#xff0c;0&#xff0c;0)GREEN(0,2…

软件性能测试之负载测试、压力测试详情介绍

负载测试和压力测试是软件性能测试中的两个重要概念&#xff0c;它们在保证软件质量和性能方面起到至关重要的作用&#xff0c;本文将从多个角度详细介绍这两种测试类型。 一、软件负载测试   负载测试是在特定条件下对软件系统进行长时间运行和大数据量处理的测试&#xff…

绝地求生PUBG更新后掉帧更新后游戏里面不显示UI的解决办法

绝地求生大家一定不陌生吧&#xff0c;这款游戏在当年可是火遍大江南北的&#xff0c;这款游戏集生存、射击、竞技与一体&#xff0c;给我们带来了很好的游戏体验。最近游戏迎来了30.1版本的更新&#xff0c;游戏内更新了不少的内容&#xff0c;而且游戏与女团进行联名&#xf…

AI大模型落地应用场景:LLM训练性能基准测试

随着 ChatGPT 的现象级走红&#xff0c;引领了AI大模型时代的变革&#xff0c;从而导致 AI 算力日益紧缺。与此同时&#xff0c;中美贸易战以及美国对华进行AI芯片相关的制裁导致 AI 算力的国产化适配势在必行。之前也分享过一些国产 AI 芯片、使用国产 AI 框架 Mindformers 基…