坏蛋Dan
知乎@坏蛋Dan
发布时间:2024.1.4

前言

昨天我们学完了测试相关的

坏蛋Dan:rust基础学习--day26

今天继续往下


一个I/O项目:构建一个命令行程序

我们来写一个命令行程序,验收下之前学的内容。

要做一个搜索工具类似grep (globally search aregularexpression andprint)。

相信大家都或多或少接触过linux服务器,查日志的时候用这个grep再好不过了。

它接收两个参数,一个是需要匹配的字符串,另一个是文件路径。

然后输出匹配的信息。

涉及到的概念

我们的程序将涉及到以下几个之前学过的概念:

  • 代码组织,比如项目结构等[2]
  • 集合(vector/string[3]
  • 错误处理[4]
  • trait和生命周期[5]
  • 测试[6]

实现步骤

  1. 获取命令行参数
  2. 保存命令行参数
  3. 根据路径读文件
  4. 匹配文件内容
  5. 输出结果

创建项目

我们来创建一个binary项目,就叫minigrep


从命令行获取数据

我们之前有用到过,在实现一个猜数游戏的时候,从命令行获取用户输入的数字。

然而这玩意儿只能接受一个参数,不过如果分两次输入也不是不行。

但是没必要,rust的标准库中有比这更好康的:std::env::args

这个argsfunction返回的是一个迭代器iterator,这个我们暂时还没学到。

目前关于迭代器我们只需要知道两点:

  1. 它返回一系列的值
  2. 它有个collect的方法可以把这个迭代器转化成一个集合。

这里有一点需要注意,那就是args这个返回的内容并不一定是有效的,而当他输入的是违法的字符时,整个程序就会直接panic

不过这个问题也是可以避免的,可以使用std::env::args_os,他可以接收非法字符,它返回的迭代器中的值的类型是OsString而非argsstring

不过我们这里不用它,因为没这个需求。我们只是做一个简单的demo。而且args_os的值会因为平台的不同而不同。

另外有一点你应该注意到了,那就是我们手动声明了args这个集合的类型,这是因为rust并不能推断出你想要的集合的类型。


存储输入的数据

既然能获得数据了,那么我们就得有个变量来存储它们。

我们可以声明两个变量或者用元组,但这都不是最好的方案。

没错,相信你已经想到了,那就是struct

这里有个点就是我用了&String.clone()这个方法,直接copy了一份堆内存也就是深复制。

为什么这么做而不是用生命周期呢?

因为这样写的代码用不了生命周期,new方法中的argsnew被调用完之后就已经失效了,而这个时候我们引用的数据随着struct还在main函数中,这个时候引用指向的地址就已经有数据问题了。

这个代码其实也能改成使用生命周期的方式

俩种都可以,看你自己。一方面是性能问题,一方面代码维护问题。

我个人更倾向于第一种写法,不过args获取应该放到main函数里,这样方便阅读。

现在我们的就能获取和保存这些数据了,接下来我们要实现查询了。


读取文件

我们直接创建一个文件poem.txt

然后复制以下内容到文件里

接下来我们就可以调用读取的api了。

我们之前其实也用过读取文件的方法,File::open

成功获取到了。

但是上面的代码有些冗杂,还记得我们在学可恢复错误的?操作符的时候最后用的一个方法吗?

直接fs::read_to_string()

简洁明了。

这样我们的文件内容就获取成功了。

接下来该轮到匹配了。


匹配字符串

poem变量目前是一个String,所以对于字符串来说,我们可以用split("\n")将它们切割成一行一行的。

其中contains可以传入一个闭包,或者&str或者char

当然,我们可以用lines替代split("\n")

实际上他俩差不多。

ok,这样我们就匹配完了,并且也最终输出到终端。


总结

今天我们一步一步的实现了这个minigrep,但是它还有挺多问题,比如现在的代码全部都堆在main.rs中,这是不优雅的,另外我们也应该引入测试,这样可以提高代码的可维护性。

还有我们目前对错误的处理都是直接expect,这样不太友好。

明天我们来优化下代码组织,然后加上驱动测试以及对错误的处理。

参考

  1. ^rust-building-a-command-line-program https://doc.rust-lang.org/book/ch12-00-an-io-project.html#an-io-project-building-a-command-line-program
  2. ^rust-code-organization https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html
  3. ^rust-collections https://doc.rust-lang.org/book/ch08-00-common-collections.html
  4. ^rust-error-handling https://doc.rust-lang.org/book/ch09-00-error-handling.html
  5. ^rust-trait-and-lifetime https://doc.rust-lang.org/book/ch10-00-generics.html
  6. ^rust-writing-tests https://doc.rust-lang.org/book/ch11-00-testing.html
  7. ^rust-accepting-command-line-arguments https://doc.rust-lang.org/book/ch12-01-accepting-command-line-arguments.html#accepting-command-line-arguments
  8. ^rust-saving-arguments https://doc.rust-lang.org/book/ch12-01-accepting-command-line-arguments.html#saving-the-argument-values-in-variables
  9. ^rust-reading-a-file https://doc.rust-lang.org/book/ch12-02-reading-a-file.html#reading-a-file

发布于 2022-12-22 21:11・IP 属地广东