目录目录结构技术栈创建新项目功能实现支持正则表达式搜索忽略某些文件类型或目录目录结构src/main.rs-主程序入口Cargo.toml-项目配置文件技术栈Rust-开发语言Cargo-Rust包管理器环境准备安装Rust及Cargo
src/main.rs
- 主程序入口Cargo.toml
- 项目配置文件Rust - 开发语言 Cargo - Rust 包管理器
环境准备 安装 Rust 及 Cargo
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
rustc --version
cargo new file_search_tool
cd file_search_tool
我们将逐步实现以下功能:
输出结果 步骤 1: 解析命令行参数
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 3 {
eprintln!("Usage: {} <keyword> <directory>", args[0]);
std::process::exit(1);
}
let keyword = &args[1];
let directory = &args[2];
// 后续代码...
}
步骤 2: 遍历目录及其子目录
use std::fs;
use std::path::Path;
// ...
let entries = fs::read_dir(directory).expect("Failed to read directory");
for entry in entries {
let entry = entry.expect("Failed to read entry");
let path = entry.path();
if path.is_dir() {
// 递归调用
search_in_directory(keyword, &path);
} else {
search_in_file(keyword, &path);
}
}
// 递归函数定义
fn search_in_directory(keyword: &str, directory: &Path) {
// 使用相同逻辑遍历子目录
let entries = fs::read_dir(directory).expect("Failed to read directory");
for entry in entries {
let entry = entry.expect("Failed to read entry");
let path = entry.path();
if path.is_dir() {
search_in_directory(keyword, &path);
} else {
search_in_file(keyword, &path);
}
}
}
步骤 3: 在文件中搜索关键词
use std::fs::File;
use std::io::{BufRead, BufReader};
// ...
fn search_in_file(keyword: &str, file_path: &Path) {
let file = File::open(file_path).expect("Failed to open file");
let reader = BufReader::new(file);
for line in reader.lines() {
let line = line.expect("Failed to read line");
if line.contains(keyword) {
println!("Found in file: {}", file_path.display());
break; // 可以选择是否继续查找其他行
}
}
}
添加依赖
我们需要添加 regex
库来支持正则表达式搜索。
修改 Cargo.toml
文件:
[package]
name = "file_search_tool"
version = "0.1.0"
edition = "2021"
[dependencies]
regex = "1.7.1"
引入 regex 库:
use regex::Regex;
修改搜索逻辑:
fn search_in_file(keyword: &str, file_path: &Path) {
let file = File::open(file_path).expect("Failed to open file");
let reader = BufReader::new(file);
let re = Regex::new(keyword).expect("Invalid regex pattern");
for line in reader.lines() {
let line = line.expect("Failed to read line");
if re.is_match(&line) {
println!("Found in file: {}", file_path.display());
break; // 可以选择是否继续查找其他行
}
}
}
增加命令行选项:
use clap::Parser;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// The keyword to search for
#[arg(short, long)]
keyword: String,
/// The directory to search in
#[arg(short, long)]
directory: String,
/// Ignore files with these extensions
#[arg(short, long, value_delimiter(','))]
ignore_ext: Option<Vec<String>>,
/// Ignore directories with these names
#[arg(short, long, value_delimiter(','))]
ignore_dir: Option<Vec<String>>,
}
修改主函数:
use clap::Parser;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// The keyword to search for
#[arg(short, long)]
keyword: String,
/// The directory to search in
#[arg(short, long)]
directory: String,
/// Ignore files with these extensions
#[arg(short, long, value_delimiter(','))]
ignore_ext: Option<Vec<String>>,
/// Ignore directories with these names
#[arg(short, long, value_delimiter(','))]
ignore_dir: Option<Vec<String>>,
}
fn main() {
let args = Args::parse();
let keyword = &args.keyword;
let directory = &args.directory;
let ignore_ext = args.ignore_ext.unwrap_or_default();
let ignore_dir = args.ignore_dir.unwrap_or_default();
let entries = fs::read_dir(directory).expect("Failed to read directory");
for entry in entries {
let entry = entry.expect("Failed to read entry");
let path = entry.path();
if path.is_dir() {
if !ignore_dir.contains(&path.file_name().unwrap().to_str().unwrap()) {
search_in_directory(keyword, &path, &ignore_ext, &ignore_dir);
}
} else {
if !ignore_ext.iter().any(|ext| path.extension().map_or(false, |e| e == ext)) {
search_in_file(keyword, &path);
}
}
}
}
fn search_in_directory(
keyword: &str,
directory: &Path,
ignore_ext: &[String],
ignore_dir: &[String],
) {
let entries = fs::read_dir(directory).expect("Failed to read directory");
for entry in entries {
let entry = entry.expect("Failed to read entry");
let path = entry.path();
if path.is_dir() {
if !ignore_dir.contains(&path.file_name().unwrap().to_str().unwrap()) {
search_in_directory(keyword, &path, ignore_ext, ignore_dir);
}
} else {
if !ignore_ext.iter().any(|ext| path.extension().map_or(false, |e| e == ext)) {
search_in_file(keyword, &path);
}
}
}
}
多线程搜索以提高性能
使用 rayon
库进行多线程搜索:
[dependencies]
rayon = "1.6.1"
修改搜索逻辑:
use rayon::prelude::*;
fn main() {
let args = Args::parse();
let keyword = &args.keyword;
let directory = &args.directory;
let ignore_ext = args.ignore_ext.unwrap_or_default();
let ignore_dir = args.ignore_dir.unwrap_or_default();
let entries = fs::read_dir(directory).expect("Failed to read directory");
entries.into_iter()
.filter_map(Result::ok)
.par_for_each(|entry| {
let path = entry.path();
if path.is_dir() {
if !ignore_dir.contains(&path.file_name().unwrap().to_str().unwrap()) {
search_in_directory(keyword, &path, &ignore_ext, &ignore_dir);
}
} else {
if !ignore_ext.iter().any(|ext| path.extension().map_or(false, |e| e == ext)) {
search_in_file(keyword, &path);
}
}
});
}
fn search_in_directory(
keyword: &str,
directory: &Path,
ignore_ext: &[String],
ignore_dir: &[String],
) {
let entries = fs::read_dir(directory).expect("Failed to read directory");
entries.into_iter()
.filter_map(Result::ok)
.par_for_each(|entry| {
let path = entry.path();
if path.is_dir() {
if !ignore_dir.contains(&path.file_name().unwrap().to_str().unwrap()) {
search_in_directory(keyword, &path, ignore_ext, ignore_dir);
}
} else {
if !ignore_ext.iter().any(|ext| path.extension().map_or(false, |e| e == ext)) {
search_in_file(keyword, &path);
}
}
});
}
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!