【一起学Rust · 项目实战】命令行IO项目minigrep——接收命令行参数与读取文件内容

简介: 【一起学Rust · 项目实战】命令行IO项目minigrep——接收命令行参数与读取文件内容



前言

本系列文章章将构建一个与文件和命令行输入/输出交互的命令行工具来练习现在一些你已经掌握的 Rust 技能。

Rust 的运行速度、安全性、单二进制文件输出和跨平台支持使其成为创建命令行程序的绝佳选择,因此我们的项目将创建一个我们自己版本的经典命令行工具:grepgrep 是 “Globally search a Regular Expression and Print.” 的首字母缩写。grep 最简单的使用场景是在特定文件中搜索指定字符串。为此,grep 获取一个文件名和一个字符串作为参数,接着读取文件并找到其中包含字符串参数的行,然后打印出这些行。


一、任务目的

使用rust制作一个简化版本的grep工具,我们希望在使用我们的程序的时候是下面这样的

minigrep searchstring example-filename.txt

其中minigrep是程序名字,searchstring 是要搜索的字符串,example-filename.txt是被搜索的文件名。在开发过程中,不能每次编译都要去build目录去找这个程序然后运行,我们一般采用的运行方式是cargo run,因此我们采用以下方式来运行上面这条命令,即使用cargo run‘来代替原来的程序名字,按照运行规则,会自动把后面的参数传递给rust

cargo run searchstring example-filename.txt

传入一个要搜索的字符串searchstring ,和一个文件名example-filename.txt,并且接收这两个参数。现在crates.io已经有很多现成的库,可以很简单的实现获取程序的参数,可以直接调用,但是我们这里正在学习这部分内容,因此这里我们自己手动实现。在后续的开发过程中,我们在优化程序时,也会将我们的代码替换成creats.io中的库来实现,用来提高程序的效率。

本期我们要实现的是读取传入的这两个参数,并且读取传入文件名的文件内容,输出到屏幕上。

我们需要实现的效果如下,在poem.txt中写入文件内容,我们写入一首诗

中山孺子妾,特以色见珍。虽然不如延年妹,亦是当时绝世人。
桃李出深井,花艳惊上春。一贵复一贱,关天岂由身。
芙蓉老秋霜,团扇羞网尘。戚姬髡发入舂市,万古共悲辛。

运行以下命令

cargo run abc poem.txt

然后屏幕会输出在poem.txt中写入的文件内容

中山孺子妾,特以色见珍。虽然不如延年妹,亦是当时绝世人。
桃李出深井,花艳惊上春。一贵复一贱,关天岂由身。
芙蓉老秋霜,团扇羞网尘。戚姬髡发入舂市,万古共悲辛。

这样就意味着我们取到了传递的命令行参数,并且通过io来读取到了文件的内容。


二、创建新项目

和以前一样,我们找个地方,打开 终端 运行以下新建项目的命令,

cargo new minigrep

然后使用vscode打开minigrep文件夹,里面就是cargo为我们创建好的基础项目。

三、读取参数值

Rust 标准库提供了一个函数std::env::args,返回一个传递给程序的命令行参数的 迭代器迭代器我们在后面会详细介绍,现在你只需记住两件事:

  • 迭代器生成一系列的值
  • 可以在迭代器上调用 collect 方法将其转换为一个集合

我们将传递给程序的命令行参数读取并收集到 vector 中,先导入标准库

use std::env;

然后在 main 中接收该参数,并且调用 collect 方法接收参数,并且存入变量 args

let args: Vec<String> = env::args().collect();

完整代码如下

use std::env;
fn main() {
    let args: Vec<String> = env::args().collect();
    println!("{:#?}", args);
}

接下来我们使用 cargo run 传入参数运行程序看看效果,

cargo run needle haystack

运行效果如下图所示,可以看到,接收到的参数一共有三个,

第一个参数是:二进制文件的名称,也是位置。后面的参数就是我们输入的参数,我们传入的参数一共有两个,但是这里给出了三个参数,这是没有问题的,传入的参数第一个是文件名,接下来才我们传入的参数


四、将参数保存至变量

由于我们将参数存入了 Vector 的变量 args 中,并且我们已知参数的数量和含义是什么,利用以前学过的知识,取到这两个参数

let query = &args[1];
    let filename = &args[2];

完整代码

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

现在运行试试效果,如下图示

五、读取文件

1. 新建文本文件

在项目目录新建文本文件,如下图所示,实际情况任意,本次仅作为例子,

文件内容如下:

中山孺子妾,特以色见珍。虽然不如延年妹,亦是当时绝世人。
桃李出深井,花艳惊上春。一贵复一贱,关天岂由身。
芙蓉老秋霜,团扇羞网尘。戚姬髡发入舂市,万古共悲辛。

2. 导入文件操作函数

文件操作是在标准库中的std::fs下提供的,使用之前我们需要将这些函数导入进来,代码如下:

use std::fs;

3. 读取文件内容

fs::read_to_string 是一个读取文件内容的函数,需要接收 filename ,返回 Result<String>,其中包含文件内容。

在上一小节的代码基础之上,我们进行修改。上节代码

use std::env;
fn main() {
    let args: Vec<String> = env::args().collect();
    println!("{:#?}", args);
    let query = &args[1];
    let filename = &args[2];
    println!("搜索 {}", query);
    println!("在文件 {}", filename);
}

我们已经从命令行参数中读取到了 filename,因此我们只需要将这个参数传递给 fs::read_to_string 就得以得到我们要的文件内容,拿到文件内容以后输出文件内容。

现在我们加入以下代码

let contents = fs::read_to_string(filename)
        .expect("读取文件出错");
    println!("读取到的文本为:\n{}", contents);

完整代码:

use std::env;
use std::fs;
fn main() {
    let args: Vec<String> = env::args().collect();
    println!("{:#?}", args);
    let query = &args[1];
    let filename = &args[2];
    println!("搜索 {}", query);
    println!("在文件 {}", filename);
    let contents = fs::read_to_string(filename)
        .expect("读取文件出错");
    println!("读取到的文本为:\n{}", contents);
}

4. 运行效果

现在我们在终端执行命令,

cargo run the poem.txt

效果如下,


总结

以上就是本节的所有内容。

通过本节学到了:

  • 如何接收命令行参数
  • 将命令行参数拆解开来

了解了怎么利用 Rust 来读取一个文件的内容,即利用标准库fs来读取文件内容,仅需传入一个 filename 即可读出文件的内容。


作业

正常来说是可以读取到两个参数的,但是怎么确保我们真的能够读到两个参数,而且程序应该给予一定的提示,需要做一些错误的处理,请你对这里的问题进行处理。

你已学会了读取文件内容的操作。现在你可以考虑以下内容:

  • 如何按行来读取文件内容
  • 如何删除一个文件
  • 如何新建文件
  • 如何给文件追加内容
  • 如何获取文件的详细信息(可选)
目录
相关文章
|
6月前
文件操作与IO(一些小项目)
文件操作与IO(一些小项目)
|
2月前
|
Rust 开发者 索引
30天拿下Rust之命令行参数
30天拿下Rust之命令行参数
52 0
|
14天前
|
存储 弹性计算 固态存储
阿里云服务器ESSD Entry系统盘测评IOPS、IO读写和时延性能参数
ESSD Entry云盘是阿里云推出的新一代云盘,具备高IOPS、低延迟和企业级数据保护能力。适用于开发与测试场景,支持按量付费和包年包月计费模式。99元和199元的ECS经济型e实例和通用算力型u1实例均采用ESSD Entry系统盘,性价比高。详细性能参数和价格请参考阿里云官方页面。
51 0
|
6月前
|
存储 监控 关系型数据库
MySQL 参数innodb_read_io_threads
`innodb_read_io_threads` 是 MySQL 数据库中 InnoDB 存储引擎的一个配置参数,它用于指定后台线程池中用于处理读取 I/O 请求的线程数量。InnoDB 存储引擎负责管理数据库的物理存储和检索,是 MySQL 最常用的存储引擎之一。 ### 参数说明 - **名称**: `innodb_read_io_threads` - **默认值**: 4 - **范围**: 1 到 64 - **动态修改**: 不能动态修改(需要重启服务器) - **适用版本**: MySQL 5.6 及以上版本 ### 作用 `innodb_read_io_threads`
592 1
|
3月前
|
开发者 C# Android开发
震惊!Xamarin 跨平台开发优势满满却也挑战重重,代码复用、熟悉语言与性能优势并存,学习曲线与差异处理何解?
【8月更文挑战第31天】Xamarin 与 C# 结合,为移动应用开发带来高效跨平台解决方案,使用单一语言和框架即可构建 iOS、Android 和 Windows 原生应用。本文通过问答形式探讨 Xamarin 和 C# 如何塑造移动开发的未来,并通过示例代码展示其实际应用。Xamarin 和 C# 的组合不仅提高了开发效率,还支持最新的移动平台功能,帮助开发者应对未来挑战,如物联网、人工智能和增强现实等领域的需求。
53 0
|
4月前
|
Linux 开发工具
CPU-IO-网络-内核参数的调优
CPU-IO-网络-内核参数的调优
73 7
|
4月前
|
Rust 测试技术 编译器
Rust与C++的区别及使用问题之Rust项目中组织目录结构的问题如何解决
Rust与C++的区别及使用问题之Rust项目中组织目录结构的问题如何解决
|
5月前
|
Rust 图形学
【unity实战】使用unity制作一个类似Rust的3D生存建造建筑系统,具有很好的吸附性(附项目源码)
【unity实战】使用unity制作一个类似Rust的3D生存建造建筑系统,具有很好的吸附性(附项目源码)
131 1
|
5月前
|
Rust
使用Cargo创建、编译与运行Rust项目
使用Cargo创建、编译与运行Rust项目
189 0
|
6月前
|
人工智能 数据挖掘 Python
Python pandas中read_csv函数的io参数
Python pandas中read_csv函数的io参数
71 5