
文章目录
本系列文章章将构建一个与文件和命令行输入/输出交互的命令行工具来练习现在一些你已经掌握的 Rust 技能。
Rust 的运行速度、安全性、单二进制文件输出和跨平台支持使其成为创建命令行程序的绝佳选择,因此我们的项目将创建一个我们自己版本的经典命令行工具:grep。grep 是 “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);
}
现在运行试试效果,如下图示

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

文件内容如下:
中山孺子妾,特以色见珍。虽然不如延年妹,亦是当时绝世人。
桃李出深井,花艳惊上春。一贵复一贱,关天岂由身。
芙蓉老秋霜,团扇羞网尘。戚姬髡发入舂市,万古共悲辛。
文件操作是在标准库中的std::fs下提供的,使用之前我们需要将这些函数导入进来,代码如下:
use std::fs;
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);
}
现在我们在终端执行命令,
cargo run the poem.txt
效果如下,

以上就是本节的所有内容。
通过本节学到了:
了解了怎么利用 Rust 来读取一个文件的内容,即利用标准库fs来读取文件内容,仅需传入一个 filename 即可读出文件的内容。
正常来说是可以读取到两个参数的,但是怎么确保我们真的能够读到两个参数,而且程序应该给予一定的提示,需要做一些错误的处理,请你对这里的问题进行处理。
你已学会了读取文件内容的操作。现在你可以考虑以下内容:
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]
我想用ruby编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere
我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"
我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘
我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)
我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="