Rust实战开发文件搜索工具

目录目录结构技术栈创建新项目功能实现支持正则表达式搜索忽略某些文件类型或目录目录结构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);
                }
            }
        });
}
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
天涯学馆
天涯学馆
0x9d6d...50d5
资深大厂程序员,12年开发经验,致力于探索前沿技术!