重构多线程搜索

This commit is contained in:
2026-05-29 15:01:24 +08:00
parent 833abc1698
commit fed28ac388
4 changed files with 78 additions and 47 deletions
+2 -2
View File
@@ -11,5 +11,5 @@ panic = "abort" # 发生崩溃时直接退出,不保留复杂的栈回溯
strip = true # 自动剥离所有的调试信息和符号表(相当于执行了 Linux 的 strip 命令)
[dependencies]
walkdir = "2"
crossbeam = "0.8"
walkdir = "2" # 目录递归遍历库
crossbeam = "0.8" # crossbeam 线程池
+67 -10
View File
@@ -1,24 +1,81 @@
use std::fs;
use std::path::Path;
use crossbeam::channel;
use walkdir::WalkDir;
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> {
let contents = fs::read_to_string(path)?;
pub fn search_in_file(query: &str, path: &Path, ignore_case: bool) -> Option<FileResult> {
let contents = match fs::read_to_string(path) {
Ok(text) => text,
Err(_) => return None
};
// 🌟 2. 跨模块调用:通过包名或相对路径调用 searcher 里的函数
let results = searcher::search(query, &contents, ignore_case);
if !results.is_empty() {
println!("\x1b[35m{:?} :\x1b[0m", path);
let total_lines = contents.lines().count();
let max_width = total_lines.to_string().len();
let mut matches_vec = Vec::new();
for (line_num, line) in results {
println!("\x1b[32m{:>width$} |\x1b[0m {}", line_num, line, width = max_width);
for (line_num, line_str) in results {
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
View File
@@ -1,8 +1,7 @@
use std::env;
use std::path::Path;
use walkdir::WalkDir;
use rustgrep::{ search_in_file, should_skip_dir, is_valid_extension };
use rustgrep::{ search_lines };
fn main() {
// 1.收集命令行参数到一个 Vector
@@ -10,7 +9,7 @@ fn main() {
// 2.简单的边界检查,防止索引越界
if args.len() < 3 {
println!("用法提示:minigrep <搜索关键字> <文件路径>");
println!("用法提示:minigrep <搜索关键字> <文件路径> [文件类型,可传入多个,用\",\"隔开,支持\"!\"反选]");
return;
}
@@ -36,36 +35,5 @@ fn main() {
eprintln!("错误:路径 '{}' 不存在", target_path);
std::process::exit(1);
}
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),
}
}
search_lines(target_path,extensions_str,real_query,ignore_case);
}
+6
View File
@@ -3,3 +3,9 @@ mod filter;
pub use core::search;
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,
}