重构多线程搜索
This commit is contained in:
+2
-2
@@ -11,5 +11,5 @@ panic = "abort" # 发生崩溃时直接退出,不保留复杂的栈回溯
|
|||||||
strip = true # 自动剥离所有的调试信息和符号表(相当于执行了 Linux 的 strip 命令)
|
strip = true # 自动剥离所有的调试信息和符号表(相当于执行了 Linux 的 strip 命令)
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
walkdir = "2"
|
walkdir = "2" # 目录递归遍历库
|
||||||
crossbeam = "0.8"
|
crossbeam = "0.8" # crossbeam 线程池
|
||||||
|
|||||||
+67
-10
@@ -1,24 +1,81 @@
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use crossbeam::channel;
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
pub mod searcher;
|
pub mod searcher;
|
||||||
pub use searcher::{ should_skip_dir, is_valid_extension };
|
pub use searcher::{ should_skip_dir, is_valid_extension, FileResult };
|
||||||
|
|
||||||
|
|
||||||
|
pub fn search_lines(root_dir: &str,extensions_str: Option<String>, real_query: &str,ignore_case: bool) {
|
||||||
|
let (tx, rx) = channel::unbounded::<FileResult>();
|
||||||
|
crossbeam::scope(|scope|{
|
||||||
|
// 使用WalkDir递归遍历路径
|
||||||
|
let it = WalkDir::new(root_dir).into_iter().filter_map(|e| e.ok());
|
||||||
|
|
||||||
|
for entry in it {
|
||||||
|
let path = entry.path().to_path_buf();
|
||||||
|
if path.is_dir() && should_skip_dir(&path) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 只搜索文件 跳过文件夹
|
||||||
|
if path.is_file() && is_valid_extension(&path, &extensions_str) {
|
||||||
|
|
||||||
|
let thread_tx = tx.clone();
|
||||||
|
|
||||||
|
scope.spawn(move |_|{
|
||||||
|
if let Some(file_results) = search_in_file(real_query, &path, ignore_case) {
|
||||||
|
let _ = thread_tx.send(file_results);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(tx);
|
||||||
|
|
||||||
|
while let Ok(file_result) = rx.recv() {
|
||||||
|
// let normalized_path = Path::new(&file_result.path_name)
|
||||||
|
// .components()
|
||||||
|
// .map(|comp| comp.as_os_str().to_string_lossy())
|
||||||
|
// .collect::<Vec<_>>()
|
||||||
|
// .join(&MAIN_SEPARATOR.to_string());
|
||||||
|
let normalized_path = file_result.path_name;
|
||||||
|
let max_width = file_result.line_count.to_string().len();
|
||||||
|
|
||||||
|
println!("\x1b[35m{:?} :\x1b[0m", normalized_path);
|
||||||
|
for (line_num, line_str) in file_result.matches {
|
||||||
|
println!("\x1b[32m{:>width$} |\x1b[0m {}", line_num, line_str, width = max_width);
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 处理单个文件搜索
|
// 处理单个文件搜索
|
||||||
pub fn search_in_file(query: &str, path: &Path, ignore_case: bool) -> Result<(), std::io::Error> {
|
pub fn search_in_file(query: &str, path: &Path, ignore_case: bool) -> Option<FileResult> {
|
||||||
let contents = fs::read_to_string(path)?;
|
let contents = match fs::read_to_string(path) {
|
||||||
|
Ok(text) => text,
|
||||||
|
Err(_) => return None
|
||||||
|
};
|
||||||
// 🌟 2. 跨模块调用:通过包名或相对路径调用 searcher 里的函数
|
// 🌟 2. 跨模块调用:通过包名或相对路径调用 searcher 里的函数
|
||||||
let results = searcher::search(query, &contents, ignore_case);
|
let results = searcher::search(query, &contents, ignore_case);
|
||||||
|
|
||||||
if !results.is_empty() {
|
if !results.is_empty() {
|
||||||
println!("\x1b[35m{:?} :\x1b[0m", path);
|
let mut matches_vec = Vec::new();
|
||||||
let total_lines = contents.lines().count();
|
|
||||||
let max_width = total_lines.to_string().len();
|
|
||||||
|
|
||||||
for (line_num, line) in results {
|
for (line_num, line_str) in results {
|
||||||
println!("\x1b[32m{:>width$} |\x1b[0m {}", line_num, line, width = max_width);
|
matches_vec.push((line_num, line_str.to_string()));
|
||||||
}
|
}
|
||||||
println!();
|
|
||||||
|
let file_line_count = contents.lines().count();
|
||||||
|
Some(FileResult {
|
||||||
|
path_name: path.to_string_lossy().replace("\\","/"),
|
||||||
|
matches: matches_vec,
|
||||||
|
line_count: file_line_count
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-35
@@ -1,8 +1,7 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use walkdir::WalkDir;
|
|
||||||
|
|
||||||
use rustgrep::{ search_in_file, should_skip_dir, is_valid_extension };
|
use rustgrep::{ search_lines };
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// 1.收集命令行参数到一个 Vector
|
// 1.收集命令行参数到一个 Vector
|
||||||
@@ -10,7 +9,7 @@ fn main() {
|
|||||||
|
|
||||||
// 2.简单的边界检查,防止索引越界
|
// 2.简单的边界检查,防止索引越界
|
||||||
if args.len() < 3 {
|
if args.len() < 3 {
|
||||||
println!("用法提示:minigrep <搜索关键字> <文件路径>");
|
println!("用法提示:minigrep <搜索关键字> <文件路径> [文件类型,可传入多个,用\",\"隔开,支持\"!\"反选]");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,36 +35,5 @@ fn main() {
|
|||||||
eprintln!("错误:路径 '{}' 不存在", target_path);
|
eprintln!("错误:路径 '{}' 不存在", target_path);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
search_lines(target_path,extensions_str,real_query,ignore_case);
|
||||||
let mut it = WalkDir::new(target_path).into_iter();
|
|
||||||
|
|
||||||
// 使用WalkDir递归遍历路径
|
|
||||||
while let Some(entry) = it.next() {
|
|
||||||
// 通过match安全处理异常
|
|
||||||
match entry {
|
|
||||||
Ok(dir_entry) => {
|
|
||||||
let path = dir_entry.path();
|
|
||||||
|
|
||||||
if path.is_dir() && should_skip_dir(path) {
|
|
||||||
it.skip_current_dir();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 只搜索文件 跳过文件夹
|
|
||||||
if path.is_file() {
|
|
||||||
if !is_valid_extension(path, &extensions_str) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = search_in_file(real_query, path, ignore_case) {
|
|
||||||
// 只在严重错误时打印警告
|
|
||||||
if e.kind() != std::io::ErrorKind::InvalidData {
|
|
||||||
eprintln!("警告: 无法读取文件 {:?}: {}", path, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => eprintln!("遍历目录时出错: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,3 +3,9 @@ mod filter;
|
|||||||
|
|
||||||
pub use core::search;
|
pub use core::search;
|
||||||
pub use filter::{ should_skip_dir, is_valid_extension };
|
pub use filter::{ should_skip_dir, is_valid_extension };
|
||||||
|
|
||||||
|
pub struct FileResult {
|
||||||
|
pub path_name: String,
|
||||||
|
pub matches: Vec<(usize, String)>,
|
||||||
|
pub line_count: usize,
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user