本文内容,根本同原书本,只是稍作了一些改动:
- 可以输入 --help展示用法
- 如果文件凌驾1MB,则会接纳逐行查找的方法,制止内存不足
整个程序包罗个文件:main.rs,lib.rs
一、代码
闲话少叙,直接上代码。
main.rs- /**
- * 完全模仿书本的例子
- *
- * 运行示例
- * cargo run 宋 E:\learning\gitee\rust-org\test-1\minigrep\src\古诗.txt -- ok
- * cargo run 宋 古诗.txt -- ok
- * cargo run 宋 -- 错误,因为参数太少了
- */
- use std::env;
- use minigrep::{config,search};
- fn main() {
- //1.0 args() 返回的字符是unicode编码的 ,args()本身返回迭代器,collect()方法将迭代器中的元素收集到Vec<String>中
- let args: Vec<String> = env::args().collect();
- //2.0 解析参数,并构建配置
- // unwrap_or_else()方法,如果Config::build(&args)返回Ok(config),则执行闭包中的内容,否则执行|err|{...}中的内容
- let con=config::build(&args).unwrap_or_else(|err|{
- eprintln!("程序异常:{}",err);
- std::process::exit(1);
- });
- if con.show_help {
- config::show_help();
- std::process::exit(0);
- }
-
- println!("配置信息:{:?}",&con);
- //3.0 根据配置查找文件,并打印查找到的结果
- //如果文件比较小,那么就直接在内存处理;否则直接在search中逐行处理
- if config::is_too_big(&con.file_name){<br>
复制代码 print!("正在查找大文件...");- let result= search::searh_big_file(&con.file_name,&con.target);
- match result{
- Ok(_)=>println!("查找完成!"),
- Err(e)=>println!("读取文件失败: {:?}", e)
- }
- }
- else{<br>
复制代码 print!("正在查找小文件...");- let result=search::search_small_file(&con);
- for line in result.iter() {
- println!("{}",line);
- }
- }
- }
复制代码
lib.rs- /**
- * 创建两个子模块
- */
- #[derive(Debug)]
- pub struct Config {
- pub file_name: String,
- pub target: String,
- pub show_help: bool,
- }
- /**
- * 模块执行配置
- */
- pub mod config {
- use super::Config;
- use std::env;
- use std::fs;
- use std::path::Path;
- /**
- * 第二个参数: 被查找的字符串
- * 第三个参数: 文件路径,可以是全路径或者不包含路径的文件名
- */
- pub fn build(args: &[String]) -> Result<Config, &str> {
- if args.len() < 3 {
- if args.len() == 2 {
- //如果target_name=="--help"则打印帮助信息,否则执行文件
- let target_name = args[1].clone();
- if target_name == "--help" {
- return Ok(Config {
- target: target_name,
- file_name: "".to_string(),
- show_help: true,
- });
- }
- }
- return Err("必须有两个参数");
- }
- println!("rust返回的程序路径:{}", args[0]);
- let target_name = args[1].clone();
- let file_path = args[2].clone();
- if !file_exists(&file_path) {
- //用户输入的是文件名
- let exec_path = get_current_exec_path();
- println!("当前执行路径:{}", exec_path);
- let mut tmp_path = exec_path.clone() + &file_path;
- if !file_exists(&tmp_path) {
- tmp_path = exec_path.clone().replace("\\target\\debug\", "\\src\") + &file_path;
- if !file_exists(&tmp_path) {
- tmp_path =
- exec_path.clone().replace("\\target\\release\", "\\src\") + &file_path;
- if !file_exists(&tmp_path) {
- return Err("文件不存在");
- }
- }
- //在src下
- Ok(Config {
- target: target_name,
- file_name: tmp_path,
- show_help: false,
- })
- } else {
- //在当前目录下=执行路径+文件名
- Ok(Config {
- target: target_name,
- file_name: tmp_path,
- show_help: false,
- })
- }
- } else {
- //在当前目录下 -- 即用户输入的是全路径
- println!("{}", file_path);
- Ok(Config {
- target: target_name,
- file_name: file_path,
- show_help: false,
- })
- }
- }
- /**
- * 判断文件是否存在,如果存在则返回true,否则返回false
- */
- fn file_exists(file_name: &String) -> bool {
- Path::new(&file_name).exists()
- }
- /**
- * 获取当前执行路径,最后会包含分隔符号\
- * 例如 d:\soft\rust-test\
- * 注意:仅限用于windows平台
- */
- fn get_current_exec_path() -> String {
- let cur_dir = match env::current_exe() {
- Ok(exe_path) => exe_path.display().to_string(),
- Err(e) => panic!("获取当前路径失败:{}", e),
- };
- // 获取cur_dir的目录,不需要包含文件名称
- let path = Path::new(&cur_dir);
- let result = path.parent().unwrap().to_str().unwrap().to_string() + "\";
- result
- }
- pub fn is_too_big(file_name: &String) -> bool {
- //判断file_name是否超过1Mb,如果是,则返回true,否则返回false
- let file_size = fs::metadata(file_name).unwrap().len();
- if file_size > 1 * 1024 * 1024 {
- return true;
- } else {
- false
- }
- }
- pub fn show_help() {
- println!("用法:minigrep target path");
- println!(" minigrep --help");
- println!("参数:");
- println!(" target 需要查找的目标字符串");
- println!(" path 包含target的文件,可以是全路径,也可以是文件名");
- println!(" 如果是文件名,则会尝试在当前目录下,或者是在相对路径..\\..\\src");
- println!(" --help 查看帮助");
- println!("示例:");
- println!(" minigrep 宋 E:\\learning\\gitee\\rust-org\\test-1\\minigrep\\src\\古诗.txt ");
- println!(" minigrep 宋 古诗.txt");
- println!("版本 1.0 for windows, 作者->lzfto");
- }
- }
- /**
- * 模块执行搜索
- */
- pub mod search {
- use super::Config;
- use std::fs;
- use std::fs::File;
- use std::io::{BufRead, BufReader};
- use std::path::Path;
- /**
- * 搜索小于1M的文件,并返回查找的结果
- * @return 返回查找的结果 Vec<String>
- */
- pub fn search_small_file(con: &Config) -> Vec<String> {
- let contents = fs::read_to_string(con.file_name.clone()).expect("文件不存在");
- println!("文章内容:\n{}", contents);
- //把contents根据换行符切割成向量
- let mut result: Vec<String> = vec![];
- let lines = contents.lines();
- for line in lines {
- if line.find(&con.target).is_some() {
- result.push(line.to_string());
- }
- }
- return result;
- }
- /**
- * 搜索大于1M的文件
- * @return 表示成功与否的信息 Result<()>
- */
- pub fn searh_big_file(file_name: &String, target: &String) -> std::io::Result<()> {
- //打开文件file_name,逐行查找,找到则直接打印
- let path = Path::new(file_name);
- let file = File::open(&path)?;
- let reader = BufReader::new(file);
- for line in reader.lines() {
- match line {
- Ok(line) => {
- if line.find(target).is_some() {
- println!("{}", line);
- }
- }
- Err(e) => println!("Error reading line: {}", e),
- }
- }
- Ok(())
- }
- }
复制代码
二、测试
为了便于测试,在src目录下放了两个文件:
古诗.txt 1kb
武林外史_utf8.txt 2120kb
表现帮助
查找小文件
查找大文件
三、小结
本例用到了截止12章节的大部分知识:
- 范例-重要是Vec,String,枚举,struct
- 结果处理Result,错误处理
- 程序组织,包括模块,use等等
- i/o-读取程序参数、读取文件,分行读取
必须得承认,rust的代码不好写,也不好看,幸好有优秀的编译器和优秀的ide帮了大忙!
这些语法对于初学者而言,还是比较别扭,充斥着怪异的符号和奇特的语法。也许rust的发明者需要泯灭一些心思再优化下编译器,让工程师能够
以更自然优雅的方式写出不违反初心的代码。工程服从也是很重要的!
在编写的过程中,对引用借用有了更具体的体会!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |