ToB企服应用市场:ToB评测及商务社交产业平台
标题:
rust学习十二、一个I/O程序练习
[打印本页]
作者:
吴旭华
时间:
2024-12-3 16:24
标题:
rust学习十二、一个I/O程序练习
本文内容,根本同原书本,只是稍作了一些改动:
可以输入 --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企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4