Rust如何提取文件扩展名 实现下载目录文件自动分类整理
Rust实现按文件扩展名自动分类移动文件
Rust标准库std::path::Path原生提供了文件扩展名提取能力,不需要手动写字符串切割逻辑,完全可以实现你要的自动分类需求。
你原有代码无法运行的核心问题有3个:
- 直接拿完整文件路径和
.jpg这类后缀字符串做相等判断,永远不可能匹配成功 - 调用
path.display()得到的是用于打印的临时显示结构,不能直接作为路径参数传给文件操作API,会触发类型错误 - 没有跳过子目录、没有处理无扩展名文件、没有错误兜底,运行时很容易直接panic崩溃
正确提取扩展名的方法
使用Path结构自带的extension()方法即可,该方法返回Option<&OsStr>类型,会自动处理路径分隔符、多段文件名等边界情况,不会出现手动切字符串容易出现的匹配错误。
基础用法示例:
use std::path::Path; let file_path = Path::new("./travel_photo.jpg"); if let Some(ext) = file_path.extension().and_then(|os_str| os_str.to_str()) { // 统一转小写,避免.JPG/.Png这类大写后缀漏匹配 match ext.to_ascii_lowercase().as_str() { "jpg" | "png" | "webp" => println!("图片文件"), "mp4" | "mkv" => println!("视频文件"), "mp3" | "flac" => println!("音频文件"), _ => println!("其他类型文件") } }
注意:extension()返回的扩展名已经去掉了前缀的.,匹配的时候直接写"jpg"即可,不要写成".jpg",否则会匹配失败。
可直接运行的修正版代码
修正版做了以下优化:
- 自动适配Windows/macOS/Linux的系统下载目录,不需要硬编码路径
- 遍历目录时自动跳过子目录,仅处理普通文件
- 扩展名统一转小写匹配,覆盖大写后缀场景
- 全流程用标准库Path API拼接路径,避免跨平台路径分隔符不兼容问题
- 增加错误兜底,单个文件操作失败不会导致整个程序崩溃
- 轮询扫描增加5秒间隔,不会占满CPU资源
use std::fs; use std::path::PathBuf; fn cls(){ print!("{esc}c", esc = 27 as char); } // 获取系统默认下载目录,无权限读取时兜底到当前程序运行目录 fn get_download_dir() -> PathBuf { if let Some(home) = dirs::home_dir() { let download_path = home.join("Downloads"); if download_path.exists() { return download_path; } } PathBuf::from("./") } fn main() { loop { cls(); let download_dir = get_download_dir(); println!("正在扫描目录: {}", download_dir.display()); // 分类规则:(分类目录名, 对应扩展名列表) let categories = [ ("Downloaded Pictures", ["jpg", "jpeg", "png", "gif", "bmp", "webp"]), ("Downloaded Videos", ["mp4", "mkv", "mov", "avi", "flv", "webm"]), ("Downloaded Music", ["mp3", "ogg", "flac", "wav", "aac"]), ("Downloaded Documents", ["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "md"]), ("Downloaded Archives", ["zip", "rar", "7z", "tar", "gz", "bz2"]) ]; let entries = match fs::read_dir(&download_dir) { Ok(e) => e, Err(err) => { println!("读取目录失败: {err}"); break; } }; for entry in entries { let entry = match entry { Ok(e) => e, Err(_) => continue }; let file_path = entry.path(); // 跳过目录、软链等非普通文件 if !file_path.is_file() { continue; } // 提取扩展名,无扩展名的文件直接跳过 let ext = match file_path.extension().and_then(|e| e.to_str()) { Some(e) => e.to_ascii_lowercase(), None => continue }; // 匹配分类并移动文件 for (target_dir_name, support_exts) in categories { if support_exts.contains(&ext.as_str()) { let target_dir = download_dir.join(target_dir_name); // 分类目录不存在时自动创建 if let Err(err) = fs::create_dir_all(&target_dir) { println!("创建目录{}失败: {}", target_dir.display(), err); continue; } let file_name = match file_path.file_name() { Some(n) => n, None => continue }; let target_path = target_dir.join(file_name); match fs::rename(&file_path, &target_path) { Ok(_) => println!("已移动: {} -> {}", file_name.to_string_lossy(), target_dir_name), Err(err) => println!("移动文件{}失败: {}", file_name.to_string_lossy(), err) } break; } } } // 间隔5秒扫描一次 std::thread::sleep(std::time::Duration::from_secs(5)); } }
使用说明
- 代码中用
dirs库获取系统家目录,需要在Cargo.toml中添加依赖:dirs = "5.0";如果不想引入第三方依赖,直接修改get_download_dir()返回你的下载目录绝对路径即可 - 可以根据自己的需求修改
categories数组里的分类目录和对应扩展名 - 如果不需要持续监控扫描,把外层
loop和最后的sleep逻辑去掉,程序就会执行一次分类后直接退出
内容的提问来源于stack exchange,提问作者user18240098
相关产品推荐
相关产品推荐

