Как получить список файлов в директории на Rust?
Правильный способ получения списка файлов в Rust
Вы почти на правильном пути! std::fs::read_dir — это именно то, что нужно. Проблема в том, что он возвращает итератор по Result, и вам нужно извлечь из каждого элемента имя файла. Вот полное решение с обработкой ошибок и фильтрацией:
use std::fs;
use std::path::Path;
fn list_files(dir: &Path) -> Result<Vec<String>, std::io::Error> {
let mut files = Vec::new();
let entries = fs::read_dir(dir)?;
for entry in entries {
let entry = entry?; // обрабатываем ошибку доступа к конкретному файлу
let path = entry.path();
// Проверяем, что это файл, а не директория
if path.is_file() {
// Извлекаем имя файла с расширением
if let Some(filename) = path.file_name() {
if let Some(name) = filename.to_str() {
files.push(name.to_string());
}
}
}
}
Ok(files)
}
fn main() -> Result<(), std::io::Error> {
let files = list_files(Path::new("."))?;
println!("Файлы в текущей директории:");
for file in &files {
println!("- {}", file);
}
Ok(())
}
Фильтрация по расширению
Если нужно отфильтровать только файлы с определённым расширением (например, .txt):
use std::path::Path;
fn list_txt_files(dir: &Path) -> Result<Vec<String>, std::io::Error> {
let mut files = Vec::new();
let entries = fs::read_dir(dir)?;
for entry in entries {
let entry = entry?;
let path = entry.path();
if path.is_file() {
// Проверяем расширение
if let Some(ext) = path.extension() {
if ext == "txt" {
if let Some(filename) = path.file_name() {
if let Some(name) = filename.to_str() {
files.push(name.to_string());
}
}
}
}
}
}
Ok(files)
}
Рекурсивный обход поддиректорий
Если нужно получить все файлы включая подпапки, добавьте рекурсию:
fn list_files_recursive(dir: &Path) -> Result<Vec<String>, std::io::Error> {
let mut files = Vec::new();
if dir.is_dir() {
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
// Рекурсивно обходим поддиректорию
let mut sub_files = list_files_recursive(&path)?;
files.append(&mut sub_files);
} else if path.is_file() {
if let Some(filename) = path.file_name() {
if let Some(name) = filename.to_str() {
files.push(name.to_string());
}
}
}
}
}
Ok(files)
}
Ключевые моменты
- Оператор
?— заменяетunwrap()и пробрасывает ошибку наверх, если она возникла. Используйте его в функциях, возвращающихResult. - Двойная обработка ошибок: сначала
fs::read_dirможет вернуть ошибку (если директория не существует), затем каждыйentryможет быть ошибкой (если файл недоступен). - Метод
path()возвращает полный путь к файлу. Используйтеfile_name()для получения только имени. - Метод
is_file()иis_dir()позволяют отличить файлы от папок. - Метод
extension()возвращает расширение файла (без точки).
Также можно использовать более идиоматичный подход с цепочками итераторов:
fn list_files_functional(dir: &Path) -> Result<Vec<String>, std::io::Error> {
fs::read_dir(dir)?
.filter_map(|entry| entry.ok()) // игнорируем ошибки доступа
.filter(|entry| entry.path().is_file())
.filter(|entry| entry.path().extension() == Some(std::ffi::OsStr::new("txt")))
.filter_map(|entry| {
entry.path()
.file_name()
.and_then(|name| name.to_str().map(|s| s.to_string()))
})
.collect()
}
Этот вариант более компактный, но менее понятный для новичков. Выбирайте тот, который вам удобнее.
<<Альтернативный подход с использованием стороннего крейта
Если вы часто работаете с файловой системой, рекомендую обратить внимание на крейт walkdir. Он предоставляет более удобный API для обхода директорий, включая рекурсивный обход, фильтрацию и обработку ошибок.
Добавьте в Cargo.toml:
[dependencies]
walkdir = "2"
Пример использования:
use walkdir::WalkDir;
fn main() {
for entry in WalkDir::new(".") {
match entry {
Ok(entry) => {
if entry.file_type().is_file() {
println!("{}", entry.file_name().to_string_lossy());
}
}
Err(e) => eprintln!("Ошибка: {}", e),
}
}
}
Преимущества:
- Автоматический рекурсивный обход (можно отключить через
.max_depth(1)). - Более читаемый код.
- Встроенная обработка ошибок с возможностью продолжить обход при ошибке доступа к отдельному файлу.
- Поддержка фильтрации по типу файла, имени, размеру и т.д.
Недостаток: дополнительная зависимость. Если вам нужен только простой список файлов, std::fs::read_dir из стандартной библиотеки — лучший выбор.
Также для простых случаев можно использовать std::fs в функциональном стиле:
use std::fs;
fn main() -> std::io::Result<()> {
let files: Vec<_> = fs::read_dir(".")?
.filter_map(|e| e.ok())
.filter(|e| e.path().is_file())
.map(|e| e.file_name().to_string_lossy().to_string())
.collect();
println!("{:?}", files);
Ok(())
} Ответов пока нет.
Войдите чтобы оставить ответ
Блоги