Rust之构建命令行程序(一):接受命令行参数

开发环境

  • Windows 10
  • Rust 1.73.0

 

  • VS Code 1.84.2

项目工程

这次创建了新的工程minigrep.

IO工程:构建命令行程序

这一章回顾了到目前为止你所学的许多技能,并探索了一些更标准的库特性。我们将构建一个与文件和命令行输入/输出交互的命令行工具,来实践一些您现在已经熟悉的Rust概念。

Rust的速度、安全性、单一二进制输出和跨平台支持使其成为创建命令行工具的理想语言,因此对于我们的项目,我们将制作自己版本的经典命令行搜索工具grep(全局搜索正则表达式并打印)。在最简单的用例中,grep在指定的文件中搜索指定的字符串。为此,grep将文件路径和字符串作为其参数。然后,它读取文件,在文件中找到包含字符串参数的行,并打印这些行。

 同时,我们将展示如何让我们的命令行工具使用许多其他命令行工具使用的终端功能。我们将读取一个环境变量的值,以允许用户配置我们工具的行为。我们还将把错误消息打印到标准错误控制台流(stderr)而不是标准输出(stdout ),例如,用户可以将成功的输出重定向到一个文件,同时仍然可以在屏幕上看到错误消息。

Rust社区成员Andrew Gallant已经创建了一个功能齐全、速度非常快的grep版本,名为ripgrep。相比之下,我们的版本会相当简单,但是本章会给你一些背景知识,你需要了解一个现实世界的项目,如ripgrep

接受命令行参数

让我们创建一个新的项目,一如既往,cargo new。我们称我们的项目为minigrep,以区别于您系统中可能已经有的grep工具。

$ cargo new minigrepCreated binary (application) `minigrep` project
$ cd minigrep

第一个任务是让minigrep接受它的两个命令行参数:文件路径和要搜索的字符串。也就是说,我们希望能够用cargo run运行我们的程序,两个连字符表示下面的参数是我们的程序而不是cargo,一个要搜索的字符串,以及一个要搜索的文件的路径,如下所示:

$ cargo run -- searchstring example-filename.txt

现在,由cargo new生成的程序无法处理我们给它的参数。crates.io上的一些现有库可以帮助编写一个接受命令行参数的程序,但是因为您刚刚学习这个概念,所以让我们自己实现这个功能。

读参数值

 为了使minigrep能够读取我们传递给它的命令行参数的值,我们需要Rust的标准库中提供的std::env::args函数。该函数返回传递给minigrep的命令行参数的迭代器。我们将在后续章节全面讨论迭代器。现在,你只需要知道迭代器的两个细节:迭代器产生一系列的值,我们可以在迭代器上调用collect方法,把它变成一个集合,比如一个vector,包含迭代器产生的所有元素。

示例12-1中的代码允许您的minigrep程序读取传递给它的任何命令行参数,然后将这些值收集到一个向量中。 

文件名:src/main.rs 

use std::env;fn main() {let args: Vec<String> = env::args().collect();dbg!(args);
}

示例12-1:将命令行参数收集到一个向量中并打印出来

首先,我们用use语句将std::env模块纳入范围,这样我们就可以使用它的args函数。注意,std::env::args函数嵌套在两层模块中。正如我们在前面的章节中所讨论的,在期望的函数嵌套在多个模块中的情况下,我们选择将父模块而不是函数引入作用域。通过这样做,我们可以轻松地使用std::env中的其他函数。这也比添加use std::env::args,然后只使用args调用函数更明确,因为args可能很容易被误认为是当前模块中定义的函数。 

args函数和无效的Unicode

请注意,如果任何参数包含无效的Unicode,std::env::args将会死机。如果您的程序需要接受包含无效Unicode的参数,请改用std::env::args_os。该函数返回一个迭代器,它产生OsString值而不是String值。为了简单起见,我们在这里选择使用std::env::args,因为OsString值因平台而异,并且比String值更复杂。 

main的第一行,我们调用env::args,并立即使用collect将迭代器转换为包含迭代器产生的所有值的向量。我们可以使用collect函数创建多种集合,因此我们显式地注释args的类型,以指定我们需要一个字符串向量。虽然我们很少需要在Rust中注释类型,但是collect是一个你经常需要注释的函数,因为Rust不能推断出你想要的集合类型。 

最后,我们使用调试宏打印向量。让我们尝试先不带参数运行代码,然后带两个参数: 

$ cargo runCompiling minigrep v0.1.0 (file:///projects/minigrep)Finished dev [unoptimized + debuginfo] target(s) in 0.61sRunning `target/debug/minigrep`
[src/main.rs:5] args = ["target/debug/minigrep",
]

$ cargo run -- needle haystackCompiling minigrep v0.1.0 (file:///projects/minigrep)Finished dev [unoptimized + debuginfo] target(s) in 1.57sRunning `target/debug/minigrep needle haystack`
[src/main.rs:5] args = ["target/debug/minigrep","needle","haystack",
]

注意,向量中的第一个值是“target/debug/minigrep”,这是我们的二进制文件的名称。这与C中参数列表的行为相匹配,允许程序使用在执行过程中被调用的名称。如果您想在消息中打印程序名,或者根据调用程序时使用的命令行别名来更改程序的行为,那么访问程序名通常会很方便。但是为了本章的目的,我们将忽略它,只保存我们需要的两个参数。

将参数值保存在变量中

该程序目前能够访问指定为命令行参数的值。现在我们需要将两个参数的值保存在变量中,这样我们就可以在程序的其余部分使用这些值。我们在示例12-2中这样做了。 

文件名:src/main.rs

use std::env;fn main() {let args: Vec<String> = env::args().collect();let query = &args[1];let file_path = &args[2];println!("Searching for {}", query);println!("In file {}", file_path);
}

 示例12-2:创建变量来保存查询参数和文件路径参数

正如我们在打印vector时看到的,程序名在args[0]处占用了vector中的第一个值,所以我们从索引1处开始参数。minigrep获取的第一个参数是我们要搜索的字符串,所以我们在变量query中引用了第一个参数。第二个参数将是文件路径,因此我们将对第二个参数的引用放在变量file_path中。 

我们临时打印这些变量的值,以证明代码按照我们的预期工作。让我们用参数testsample.txt再次运行这个程序:

$ cargo run -- test sample.txtCompiling minigrep v0.1.0 (file:///projects/minigrep)Finished dev [unoptimized + debuginfo] target(s) in 0.0sRunning `target/debug/minigrep test sample.txt`
Searching for test
In file sample.txt

太好了,程序工作了!我们需要的参数值被保存到正确的变量中。稍后我们将添加一些错误处理来处理某些潜在的错误情况,比如当用户没有提供参数时;现在,我们将忽略这种情况,转而添加文件读取功能。

本章重点

  • 构建命令行程序的概念
  • 如何构建命令行程序
  • 如何通过程序读取参数值
  • 如何通过程序讲参数值保存在变量中

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

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

相关文章

openssl版本号解析

https://www.openssl.org/docs/man1.1.1/man3/OPENSSL_VERSION_NUMBER.html #define OPENSSL_VERSION_NUMBER 0xnnnnnnnnnL 从上面文章中可以看得出版本号共9位数&#xff08;最后的L表示长整型&#xff09; 如 0x101010ef 省略了前面的一个0, 实际为 0x0101010ef 版本是 1.…

Pangolin报错

【Error】error: ‘decay_t’ is not a member of ‘std’; did you mean ‘decay’? 解决办法 CMakeLists指定c版本为c14以上 set( CMAKE_CXX_FLAGS "-stdc14" )gcc版本高于4.9以上

业务流程图用什么软件绘制?

在企业的日常工作中&#xff0c;对于业务流程的把控和优化显得非常重要。为了更好地理解和管理业务流程&#xff0c;业务流程图便应运而生。 业务流程图是企业管理的图形化工具&#xff0c;它描述了企业在生产和服务提供过程中&#xff0c;在各个环节中所涉及的各种操作、任务…

python如何抓取携程酒店的价格,让工作更简单点

有时候老板没事安排点事&#xff0c;为了偷懒&#xff0c;只能使出大招&#xff0c;毕竟自己不是那么老老实实干活的人&#xff0c;整理数据这类累和繁琐的活&#xff0c;我怎么能轻易动&#xff0c;好在gpt可以帮我来实现&#xff0c;有人可能会说&#xff0c;这么点内容你还不…

mac mini只有键盘怎么连接蓝牙鼠标

刚才遇到个问题&#xff0c;手头只有一个键盘和一个蓝牙鼠标&#xff0c;但是最新版本的系统对默认的纯键盘控制不太友好&#xff0c;得自己试出怎么连接&#xff0c;这里把如何在mac mini上用一个键盘连接上蓝牙鼠标&#xff0c;手上这台mini系统版本是14.0&#xff0c;有需要…

Lua判断字符串包含另一个字符串

string.find(“原字符串”,“目标字符串”) 返回这个子串的起始索引和结束索引&#xff0c;否则就会返回nil local index sting.find("ABCD",AB) --结果 1 2 if(index ~ nil)return true endstring.match(“原字符串”,“目标字符串”) local result string.mat…

linux常见命令合集

layout: post # 使用的布局&#xff08;不需要改&#xff09; title: linux使用命令合集 # 标题 subtitle: linux常用命令 #副标题 date: 2023-11-20 # 时间 author: BY ThreeStones1029 # 作者 header-img: img/about_bg.jpg #这篇文章标题背景图片 catalog: true # 是否归档 …

Pytorch:torch.Generator()

PyTorch 通过 torch.Generator 类来操作随机数的生成 1. 默认的随机数生成器 import torch# 设置默认的随机数种子 torch.manual_seed(0)# 查看默认的随机数种子 torch.initial_seed()2. 指定 torch.Generator随机数生成器 g torch.Generator() torch.Generator 实例的方法…

OpenCvSharp从入门到实践-(05)通道

目录 1、拆分通道 1.1、实例1-拆分一副BGR图像的通道 1.2、实例2-拆分一副HSV图像的通道 2、合并通道 2.1 实例3-合并通道的顺序不同&#xff0c;图像的显示效果也不通 实例4-合格H通道图像、S通道图像、V通道图像 3、综合运用拆分通道和合并通道 2.2、实例5-只把H通道的…

C++ new / delete ,malloc / free 区别

一、概述 都可以⽤来在堆上分配和回收空间。new /delete 是操作符&#xff0c;malloc/free 是库函数。 二、执行new的过程 1、分配未初始化的内存空间&#xff08;malloc) 2、使⽤对象的构造函数对空间进⾏初始 化&#xff0c;返回空间的⾸地址。 注意&#xff1a;如果在第…

Vue3-admin-template 导入模板功能

先看效果&#xff1a; 直接上代码&#xff1a; 1.绑定事件&#xff1a; <el-button type"primary" click"templates">模板导入</el-button> 2.写结构样式 <!-- 模板导入 --><el-dialog v-model"Statusimprot" title&quo…

js相同字符串截取拼接

原数据 const list [999-1234567801,999-1234567802,999-1234567803, ]; const list1 [999-1234567899,999-1234567900,999-1234567901, ];期望数据 999-1234567801/2/3 //list 999-1234567899/900/901 //list1处理代码 // 连续号码处理 export const formatNumber (tick…

9.二维数组——打印出杨辉三角形(要求打印出10行)

文章目录 前言一、题目描述 二、题目分析 三、解题 程序运行代码 前言 本系列为二维数组编程题&#xff0c;点滴成长&#xff0c;一起逆袭。 一、题目描述 打印出杨辉三角形&#xff08;要求打印出10行&#xff09;。 二、题目分析 三、解题 程序运行代码 #include<s…

C++求出给定数组中第k大的元素

可以利用快速选择算法来解决。快速选择算法是基于快速排序算法的一种变种&#xff0c;它可以在O(n)的时间复杂度内找到第k大的元素。 快速选择算法的基本思路是选择一个pivot元素&#xff0c;将数组分成小于pivot和大于pivot两部分。如果小于pivot的元素个数大于等于k&#xf…

在柯桥西班牙语论文写作,连接词只会用porquepero?西语连接词大全来啦~

Adicin -agregan nuevos datos al desarrollo de una idea o introduce otro aspecto del tema. 为观点的论述增添新的信息&#xff0c;或介绍主题的另一个方面。 1 Lista de conectores 连接词列表 Adems. As mismo. Hay que mencionar, adems. Habra que decir tambin. Mas …

深度学习常用指令(Anaconda、Python)

1. Anaconda 创建环境&#xff1a; conda create -n ENV_NAME pythonx.x 删除环境&#xff1a; conda remove -n ENV_NAME --all 删除环境后还需要找到该环境所在文件夹&#xff0c;并手动将其删除 查看有哪些环境&#xff1a; conda env list 激活环境&#xff1a; c…

[算法总结] - 蓄水池采样算法

问题描述 在长度为N的数组中&#xff0c;随机等概率选取K个元素&#xff0c;如何实现这个随机算法。 思路很简单&#xff0c;生成一个[0, N]的随机数index&#xff0c;然后返回index上的数值即可。 但是&#xff0c;如果输入是一个长度未知的数组比如stream&#xff0c;先遍历…

JUC(Java.util.concurrent)的常见类

目录 ♫ReentrantLock ♪什么是ReentrantLock ♪ReentrantLock的用法 ♪ReentrantLock和synchronized的区别 ♫Semaphore ♪什么是Semaphore ♪semaphore的用法 ♫CountDownLatch ♪什么是CountDownLatch ♪CountDownLatch的使用 ♫多线程环境使用ArrayList ♫多线程环…

如何在Excel表格中找回密码

最简单的excel表格解密方法是通过密码帝官网。具体步骤如下&#xff1a;第一步百度搜索【 密码帝官网 】&#xff0c;第二步在用户中心上传需要解密的文件即可。 这个方法安全且简单易操作&#xff0c;不用下载软件&#xff0c;适用于手机和电脑。如果你的excel表格不能编辑&a…

数据分享 I 重点城市现状建筑数据,shp格式放送

数据名称: 现状建筑数据 数据格式: Shp 数据时间: 不同城市的数据时间有所不同&#xff0c;详情可搜“吧唧数据” 数据几何类型: 面 数据坐标系: WGS84坐标系 数据来源&#xff1a;网络公开数据 深圳市现状建筑数据示意图 东莞市部分镇街现状建筑数据示意图 武汉市部…