rust学习十二、一个I/O程序练习
1.rust学习一、入门之搭建简单开发环境2.rust学习二、入门之运行单个脚本3.rust学习四、控制语句4.rust学习五、认识所有权5.rust学习三、基本类型6.rust学习六、简单的struct结构7.rust学习七、枚举8.rust学习八、包和模块9.rust学习九.1、集合之向量10.rust学习九.2、集合之字符串11.rust学习九.3、集合之哈希映射表12.rust学习十、异常处理(错误处理)13.rust学习十一.1、泛型(通用类型)14.rust学习十一.2、利用Trait(特质)定义通用类型的共同行为15.rust学习十一.3、生命周期标记16.rust学习十二、测试
17.rust学习十二、一个I/O程序练习
18.rust学习十三.1、匿名函数(闭包)19.rust学习十三.2、迭代器20.rust学习十四.1、编译和发布单元包Crate21.rust学习十四.2、工作空间(workspace)22.rust学习十五.1、智能指针基本概念23.rust学习十五.2、智能指针之Box(盒子)指针24.rust学习十五.3、智能指针相关的Deref和Drop特质25.rust学习十五.4、Rc和RefCell指针26.rust学习十五.5、引用循环和内存泄露27.rust学习十六.1、并发-乱弹和一个简单并发例子28.rust学习十六.2、并发-利用消息传递进行线程间通讯29.rust学习十六.3、并发-线程之间共享数据30.rust学习十七.1、async和await31.rust学习十八.1、RUST的OOP和简单示例32.rust学习十九.1、模式匹配(match patterns)本文内容,基本同原书本,只是稍作了一些改动:
- 可以输入 --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){
print!("正在查找大文件...");
let result= search::searh_big_file(&con.file_name,&con.target); match result{ Ok(_)=>println!("查找完成!"), Err(e)=>println!("读取文件失败: {:?}", e) } } else{
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的发明者需要花费一些心思再优化下编译器,让工程师能够
以更自然优雅的方式写出不违背初心的代码。工程效率也是很重要的!
在编写的过程中,对引用借用有了更具体的体会!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 易语言 —— 开山篇