rust学习十二、一个I/O程序练习

打印 上一主题 下一主题

主题 889|帖子 889|积分 2667

本文内容,根本同原书本,只是稍作了一些改动:

  • 可以输入 --help展示用法
  • 如果文件凌驾1MB,则会接纳逐行查找的方法,制止内存不足
整个程序包罗个文件:main.rs,lib.rs
一、代码

闲话少叙,直接上代码。
main.rs
  1. /**
  2. * 完全模仿书本的例子
  3. *
  4. * 运行示例
  5. * cargo run 宋 E:\learning\gitee\rust-org\test-1\minigrep\src\古诗.txt  -- ok
  6. * cargo run 宋 古诗.txt  -- ok
  7. * cargo run 宋   -- 错误,因为参数太少了
  8. */
  9. use std::env;
  10. use minigrep::{config,search};
  11. fn main() {
  12.     //1.0 args() 返回的字符是unicode编码的 ,args()本身返回迭代器,collect()方法将迭代器中的元素收集到Vec<String>中                                                                           
  13.     let args: Vec<String> = env::args().collect();
  14.     //2.0 解析参数,并构建配置
  15.     // unwrap_or_else()方法,如果Config::build(&args)返回Ok(config),则执行闭包中的内容,否则执行|err|{...}中的内容
  16.     let con=config::build(&args).unwrap_or_else(|err|{
  17.         eprintln!("程序异常:{}",err);
  18.         std::process::exit(1);
  19.     });   
  20.     if con.show_help {
  21.         config::show_help();
  22.         std::process::exit(0);
  23.     }
  24.    
  25.     println!("配置信息:{:?}",&con);
  26.     //3.0 根据配置查找文件,并打印查找到的结果
  27.     //如果文件比较小,那么就直接在内存处理;否则直接在search中逐行处理
  28.     if config::is_too_big(&con.file_name){<br>
复制代码
print!("正在查找大文件...");
  1.        let result= search::searh_big_file(&con.file_name,&con.target);
  2.        match result{
  3.         Ok(_)=>println!("查找完成!"),
  4.         Err(e)=>println!("读取文件失败: {:?}", e)
  5.        }
  6.     }
  7.     else{<br>
复制代码
print!("正在查找小文件...");
  1.         let result=search::search_small_file(&con);
  2.         for line in result.iter() {
  3.             println!("{}",line);
  4.         }
  5.     }
  6. }
复制代码
 
 
lib.rs
  1. /**
  2. * 创建两个子模块
  3. */
  4. #[derive(Debug)]
  5. pub struct Config {
  6.     pub file_name: String,
  7.     pub target: String,
  8.     pub show_help: bool,
  9. }
  10. /**
  11. * 模块执行配置
  12. */
  13. pub mod config {
  14.     use super::Config;
  15.     use std::env;
  16.     use std::fs;
  17.     use std::path::Path;
  18.     /**
  19.      * 第二个参数: 被查找的字符串
  20.      * 第三个参数: 文件路径,可以是全路径或者不包含路径的文件名
  21.      */
  22.     pub fn build(args: &[String]) -> Result<Config, &str> {
  23.         if args.len() < 3 {
  24.             if args.len() == 2 {
  25.                 //如果target_name=="--help"则打印帮助信息,否则执行文件
  26.                 let target_name = args[1].clone();
  27.                 if target_name == "--help" {
  28.                     return Ok(Config {
  29.                         target: target_name,
  30.                         file_name: "".to_string(),
  31.                         show_help: true,
  32.                     });
  33.                 }
  34.             }
  35.             return Err("必须有两个参数");
  36.         }
  37.         println!("rust返回的程序路径:{}", args[0]);
  38.         let target_name = args[1].clone();
  39.         let file_path = args[2].clone();
  40.         if !file_exists(&file_path) {
  41.             //用户输入的是文件名
  42.             let exec_path = get_current_exec_path();
  43.             println!("当前执行路径:{}", exec_path);
  44.             let mut tmp_path = exec_path.clone() + &file_path;
  45.             if !file_exists(&tmp_path) {
  46.                 tmp_path = exec_path.clone().replace("\\target\\debug\", "\\src\") + &file_path;
  47.                 if !file_exists(&tmp_path) {
  48.                     tmp_path =
  49.                         exec_path.clone().replace("\\target\\release\", "\\src\") + &file_path;
  50.                     if !file_exists(&tmp_path) {
  51.                         return Err("文件不存在");
  52.                     }
  53.                 }
  54.                 //在src下
  55.                 Ok(Config {
  56.                     target: target_name,
  57.                     file_name: tmp_path,
  58.                     show_help: false,
  59.                 })
  60.             } else {
  61.                 //在当前目录下=执行路径+文件名
  62.                 Ok(Config {
  63.                     target: target_name,
  64.                     file_name: tmp_path,
  65.                     show_help: false,
  66.                 })
  67.             }
  68.         } else {
  69.             //在当前目录下 -- 即用户输入的是全路径
  70.             println!("{}", file_path);
  71.             Ok(Config {
  72.                 target: target_name,
  73.                 file_name: file_path,
  74.                 show_help: false,
  75.             })
  76.         }
  77.     }
  78.     /**
  79.      * 判断文件是否存在,如果存在则返回true,否则返回false
  80.      */
  81.     fn file_exists(file_name: &String) -> bool {
  82.         Path::new(&file_name).exists()
  83.     }
  84.     /**
  85.      * 获取当前执行路径,最后会包含分隔符号\
  86.      * 例如 d:\soft\rust-test\
  87.      * 注意:仅限用于windows平台
  88.      */
  89.     fn get_current_exec_path() -> String {
  90.         let cur_dir = match env::current_exe() {
  91.             Ok(exe_path) => exe_path.display().to_string(),
  92.             Err(e) => panic!("获取当前路径失败:{}", e),
  93.         };
  94.         // 获取cur_dir的目录,不需要包含文件名称
  95.         let path = Path::new(&cur_dir);
  96.         let result = path.parent().unwrap().to_str().unwrap().to_string() + "\";
  97.         result
  98.     }
  99.     pub fn is_too_big(file_name: &String) -> bool {
  100.         //判断file_name是否超过1Mb,如果是,则返回true,否则返回false
  101.         let file_size = fs::metadata(file_name).unwrap().len();
  102.         if file_size > 1 * 1024 * 1024 {
  103.             return true;
  104.         } else {
  105.             false
  106.         }
  107.     }
  108.     pub fn show_help() {
  109.         println!("用法:minigrep target path");
  110.         println!("     minigrep --help");
  111.         println!("参数:");
  112.         println!("  target  需要查找的目标字符串");
  113.         println!("  path    包含target的文件,可以是全路径,也可以是文件名");
  114.         println!("          如果是文件名,则会尝试在当前目录下,或者是在相对路径..\\..\\src");
  115.         println!("  --help  查看帮助");
  116.         println!("示例:");
  117.         println!("  minigrep 宋 E:\\learning\\gitee\\rust-org\\test-1\\minigrep\\src\\古诗.txt ");
  118.         println!("  minigrep 宋 古诗.txt");
  119.         println!("版本 1.0 for windows, 作者->lzfto");
  120.     }
  121. }
  122. /**
  123. * 模块执行搜索
  124. */
  125. pub mod search {
  126.     use super::Config;
  127.     use std::fs;
  128.     use std::fs::File;
  129.     use std::io::{BufRead, BufReader};
  130.     use std::path::Path;
  131.     /**
  132.      * 搜索小于1M的文件,并返回查找的结果
  133.      * @return  返回查找的结果 Vec<String>
  134.      */
  135.     pub fn search_small_file(con: &Config) -> Vec<String> {
  136.         let contents = fs::read_to_string(con.file_name.clone()).expect("文件不存在");
  137.         println!("文章内容:\n{}", contents);
  138.         //把contents根据换行符切割成向量
  139.         let mut result: Vec<String> = vec![];
  140.         let lines = contents.lines();
  141.         for line in lines {
  142.             if line.find(&con.target).is_some() {
  143.                 result.push(line.to_string());
  144.             }
  145.         }
  146.         return result;
  147.     }
  148.     /**
  149.      * 搜索大于1M的文件
  150.      * @return  表示成功与否的信息 Result<()>
  151.      */
  152.     pub fn searh_big_file(file_name: &String, target: &String) -> std::io::Result<()> {
  153.         //打开文件file_name,逐行查找,找到则直接打印
  154.         let path = Path::new(file_name);
  155.         let file = File::open(&path)?;
  156.         let reader = BufReader::new(file);
  157.         for line in reader.lines() {
  158.             match line {
  159.                 Ok(line) => {
  160.                     if line.find(target).is_some() {
  161.                         println!("{}", line);
  162.                     }
  163.                 }
  164.                 Err(e) => println!("Error reading line: {}", e),
  165.             }
  166.         }
  167.         Ok(())
  168.     }
  169. }
复制代码
 
 
二、测试

为了便于测试,在src目录下放了两个文件:

古诗.txt  1kb
武林外史_utf8.txt 2120kb
表现帮助

 
查找小文件

 
查找大文件

 
三、小结

本例用到了截止12章节的大部分知识:

  • 范例-重要是Vec,String,枚举,struct
  • 结果处理Result,错误处理
  • 程序组织,包括模块,use等等
  • i/o-读取程序参数、读取文件,分行读取
必须得承认,rust的代码不好写,也不好看,幸好有优秀的编译器和优秀的ide帮了大忙!
这些语法对于初学者而言,还是比较别扭,充斥着怪异的符号和奇特的语法。也许rust的发明者需要泯灭一些心思再优化下编译器,让工程师能够
以更自然优雅的方式写出不违反初心的代码。工程服从也是很重要的!
在编写的过程中,对引用借用有了更具体的体会!
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

吴旭华

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表