Elysia Tools

在一个清晰的工作台里浏览工具、案例、可视化和主题指南。

热门分类

专题

语言
ZHESPTRUFRDEEN

Elysia Tools

导航

Rust

Web Rust 文件操作示例

Web Rust 文件操作示例,包括文本文件读写、文件复制移动、目录遍历和文件验证

案例

这个案例集合中的条目

代码rust简单

文本文件读写

使用Rust std::fs和std::io读取和写入各种编码选项的文本文件

难度

4/10

预计时间

20 min

标签

rust, web, file operations

前置条件

Basic Rust, File I/O, std::fs

rust
// Web Rust Text File Read/Write Examples
// Using Rust std::fs and std::io for file operations

use std::fs::{self, File, OpenOptions};
use std::io::{self, BufRead, BufReader, BufWriter, Read, Write};
use std::path::{Path, PathBuf};
use std::collections::HashMap;

// 1. Reading Text Files

/// Read entire text file content
fn read_text_file(file_path: &str) -> io::Result<String> {
    fs::read_to_string(file_path)
}

/// Read text file with specific encoding (UTF-8 validation)
fn read_text_file_with_encoding(file_path: &str) -> io::Result<String> {
    let content = fs::read_to_string(file_path)?;
    // Rust strings are UTF-8 by default, invalid sequences will cause error
    Ok(content)
}

/// Read text file line by line
fn read_text_file_line_by_line(file_path: &str) -> io::Result<Vec<String>> {
    let file = File::open(file_path)?;
    let reader = BufReader::new(file);

    let mut lines = Vec::new();
    for line in reader.lines() {
        lines.push(line?);
    }

    Ok(lines)
}

/// Read text file in chunks
fn read_text_file_in_chunks(file_path: &str, chunk_size: usize) -> io::Result<Vec<String>> {
    let file = File::open(file_path)?;
    let mut reader = BufReader::new(file);

    let mut chunks = Vec::new();
    loop {
        let mut buffer = vec![0u8; chunk_size];
        let n = reader.read(&mut buffer)?;

        if n == 0 {
            break;
        }

        buffer.truncate(n);
        chunks.push(String::from_utf8_lossy(&buffer).to_string());
    }

    Ok(chunks)
}

/// Read text file using buffered I/O
fn read_text_file_with_buffer(file_path: &str) -> io::Result<String> {
    let file = File::open(file_path)?;
    let mut reader = BufReader::new(file);

    let mut content = String::new();
    reader.read_to_string(&mut content)?;

    Ok(content)
}

// 2. Writing Text Files

/// Write text content to a file
fn write_text_file(file_path: &str, content: &str) -> io::Result<()> {
    fs::write(file_path, content)
}

/// Write a list of lines to a text file
fn write_text_lines(file_path: &str, lines: &[String]) -> io::Result<()> {
    let file = File::create(file_path)?;
    let mut writer = BufWriter::new(file);

    for line in lines {
        writeln!(writer, "{}", line)?;
    }

    writer.flush()
}

/// Write text file with permissions (Unix-like systems)
#[cfg(unix)]
fn write_text_file_with_permissions(file_path: &str, content: &str, permissions: u32) -> io::Result<()> {
    use std::os::unix::fs::PermissionsExt;

    fs::write(file_path, content)?;

    let mut perms = fs::metadata(file_path)?.permissions();
    perms.set_mode(permissions);
    fs::set_permissions(file_path, perms)?;

    Ok(())
}

/// Append text content to an existing file
fn append_to_text_file(file_path: &str, content: &str) -> io::Result<()> {
    let mut file = OpenOptions::new()
        .append(true)
        .create(true)
        .open(file_path)?;

    writeln!(file, "{}", content)
}

/// Write text file atomically (using temp file)
fn write_text_file_atomic(file_path: &str, content: &str) -> io::Result<()> {
    let temp_path = format!("{}.tmp", file_path);

    // Write to temporary file
    fs::write(&temp_path, content)?;

    // Rename atomically
    fs::rename(&temp_path, file_path)?;

    Ok(())
}

// 3. File Info Helper

/// Get file size in bytes
fn get_file_size(file_path: &str) -> io::Result<u64> {
    let metadata = fs::metadata(file_path)?;
    Ok(metadata.len())
}

/// Get file extension
fn get_file_extension(file_path: &str) -> &str {
    Path::new(file_path)
        .extension()
        .and_then(|s| s.to_str())
        .unwrap_or("")
}

/// Get file name without extension
fn get_file_name_without_extension(file_path: &str) -> String {
    Path::new(file_path)
        .file_stem()
        .and_then(|s| s.to_str())
        .unwrap_or("")
        .to_string()
}

/// Format file size in human-readable format
fn format_file_size(bytes: u64) -> String {
    const KB: u64 = 1024;
    const MB: u64 = KB * 1024;
    const GB: u64 = MB * 1024;
    const TB: u64 = GB * 1024;

    if bytes >= TB {
        format!("{:.2} TB", bytes as f64 / TB as f64)
    } else if bytes >= GB {
        format!("{:.2} GB", bytes as f64 / GB as f64)
    } else if bytes >= MB {
        format!("{:.2} MB", bytes as f64 / MB as f64)
    } else if bytes >= KB {
        format!("{:.2} KB", bytes as f64 / KB as f64)
    } else {
        format!("{} B", bytes)
    }
}

/// Get detailed information about a file
fn get_file_details(file_path: &str) -> io::Result<HashMap<String, String>> {
    let metadata = fs::metadata(file_path)?;
    let path = Path::new(file_path);
    let absolute_path = fs::canonicalize(file_path)?;

    let mut details = HashMap::new();
    details.insert("name".to_string(), path.file_name()
        .and_then(|s| s.to_str())
        .unwrap_or("")
        .to_string());
    details.insert("size".to_string(), metadata.len().to_string());
    details.insert("readable_size".to_string(), format_file_size(metadata.len()));
    details.insert("extension".to_string(), get_file_extension(file_path).to_string());
    details.insert("modified_time".to_string(), format!("{:?}", metadata.modified()));
    details.insert("is_file".to_string(), metadata.is_file().to_string());
    details.insert("is_dir".to_string(), metadata.is_dir().to_string());
    details.insert("absolute_path".to_string(), absolute_path.to_str()
        .unwrap_or("")
        .to_string());

    Ok(details)
}

// 4. File Validation

/// Check if a file exists
fn file_exists(file_path: &str) -> bool {
    Path::new(file_path).exists()
}

/// Check if path is a file
fn is_file(file_path: &str) -> bool {
    Path::new(file_path).is_file()
}

/// Check if path is a directory
fn is_directory(dir_path: &str) -> bool {
    Path::new(dir_path).is_dir()
}

/// Validate file properties
fn validate_file(file_path: &str, max_size_mb: u64) -> io::Result<HashMap<String, serde_json::Value>> {
    let mut result = HashMap::new();
    result.insert("valid".to_string(), serde_json::Value::Bool(true));
    result.insert("errors".to_string(), serde_json::Value::Array(vec![]));

    if !file_exists(file_path) {
        result.insert("valid".to_string(), serde_json::Value::Bool(false));
        result.get_mut("errors").unwrap()
            .as_array_mut().unwrap()
            .push(serde_json::json!("File does not exist"));
        return Ok(result);
    }

    if !is_file(file_path) {
        result.insert("valid".to_string(), serde_json::Value::Bool(false));
        result.get_mut("errors").unwrap()
            .as_array_mut().unwrap()
            .push(serde_json::json!("Path is not a file"));
        return Ok(result);
    }

    if max_size_mb > 0 {
        let size = get_file_size(file_path)?;
        let size_mb = size / (1024 * 1024);

        if size_mb > max_size_mb {
            result.insert("valid".to_string(), serde_json::Value::Bool(false));
            result.get_mut("errors").unwrap()
                .as_array_mut().unwrap()
                .push(serde_json::json!(format!(
                    "File size ({:.2}MB) exceeds limit ({}MB)",
                    size_mb, max_size_mb
                )));
        }
    }

    Ok(result)
}

// 5. Batch File Operations

/// Read multiple text files
fn read_multiple_files(file_paths: &[String]) -> HashMap<String, String> {
    let mut results = HashMap::new();

    for file_path in file_paths {
        if let Ok(content) = read_text_file(file_path) {
            results.insert(file_path.clone(), content);
        }
    }

    results
}

/// Write to multiple files
fn batch_write_files(file_data_map: &HashMap<String, String>) -> (Vec<String>, Vec<String>) {
    let mut successful = Vec::new();
    let mut failed = Vec::new();

    for (file_path, content) in file_data_map {
        match write_text_file(file_path, content) {
            Ok(_) => successful.push(file_path.clone()),
            Err(_) => failed.push(file_path.clone()),
        }
    }

    (successful, failed)
}

/// Search for text in multiple files
fn search_in_files(
    file_paths: &[String],
    search_text: &str,
    case_sensitive: bool,
) -> HashMap<String, Vec<usize>> {
    let mut results = HashMap::new();

    for file_path in file_paths {
        if let Ok(lines) = read_text_file_line_by_line(file_path) {
            let mut matching_lines = Vec::new();

            for (i, line) in lines.iter().enumerate() {
                let search_line = if case_sensitive {
                    line.clone()
                } else {
                    line.to_lowercase()
                };

                let search_term = if case_sensitive {
                    search_text.to_string()
                } else {
                    search_text.to_lowercase()
                };

                if search_line.contains(&search_term) {
                    matching_lines.push(i + 1);
                }
            }

            if !matching_lines.is_empty() {
                results.insert(file_path.clone(), matching_lines);
            }
        }
    }

    results
}

// 6. File Encoding Utilities

/// Detect file encoding (simplified)
fn detect_file_encoding(file_path: &str) -> io::Result<String> {
    let content = fs::read(file_path)?;

    // Check for UTF-8 BOM
    if content.len() >= 3 && content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF {
        return Ok("UTF-8 with BOM".to_string());
    }

    // Try to parse as UTF-8
    if std::str::from_utf8(&content).is_ok() {
        return Ok("UTF-8".to_string());
    }

    Ok("ASCII/Unknown".to_string())
}

/// Convert file encoding (simplified - currently no-op)
fn convert_file_encoding(
    input_path: &str,
    output_path: &str,
    _from_encoding: &str,
    _to_encoding: &str,
) -> io::Result<()> {
    let content = fs::read_to_string(input_path)?;
    fs::write(output_path, content)?;
    Ok(())
}

// Usage Examples
fn main() -> io::Result<()> {
    println!("=== Web Rust Text File Read/Write Examples ===\n");

    // 1. Read text file
    println!("--- 1. Read Text File ---");
    match read_text_file("example.txt") {
        Ok(content) => println!("Content length: {} characters", content.len()),
        Err(e) => println!("Error: {}", e),
    }

    // 2. Read line by line
    println!("\n--- 2. Read Line by Line ---");
    match read_text_file_line_by_line("example.txt") {
        Ok(lines) => {
            println!("Number of lines: {}", lines.len());
            let display_count = std::cmp::min(3, lines.len());
            println!("First {} lines: {:?}", display_count, &lines[..display_count]);
        }
        Err(e) => println!("Error: {}", e),
    }

    // 3. Write text file
    println!("\n--- 3. Write Text File ---");
    match write_text_file("output.txt", "Hello, World!\nThis is a new file.") {
        Ok(_) => println!("File written successfully"),
        Err(e) => println!("Error: {}", e),
    }

    // 4. Write lines
    println!("\n--- 4. Write Lines ---");
    let lines_to_write = vec!["Line 1".to_string(), "Line 2".to_string(), "Line 3".to_string()];
    match write_text_lines("lines.txt", &lines_to_write) {
        Ok(_) => println!("Lines file written successfully"),
        Err(e) => println!("Error: {}", e),
    }

    // 5. Get file details
    println!("\n--- 5. File Details ---");
    match get_file_details("example.txt") {
        Ok(details) => {
            println!("Name: {}", details.get("name").unwrap_or(&"?".to_string()));
            println!("Size: {}", details.get("readable_size").unwrap_or(&"?".to_string()));
            println!("Extension: {}", details.get("extension").unwrap_or(&"?".to_string()));
        }
        Err(e) => println!("Error: {}", e),
    }

    // 6. File validation
    println!("\n--- 6. File Validation ---");
    match validate_file("example.txt", 1) {
        Ok(validation) => {
            println!("Valid: {}", validation.get("valid").unwrap_or(&serde_json::Value::Bool(false)));
        }
        Err(e) => println!("Error: {}", e),
    }

    println!("\n=== All Text File Operations Completed ===");
    Ok(())
}
代码rust中等

文件复制/移动

使用进度跟踪和错误处理复制和移动文件

难度

6/10

预计时间

25 min

标签

rust, web, file operations

前置条件

Intermediate Rust, std::io, std::fs

rust
// Web Rust File Copy/Move Examples
// File copy and move operations with progress tracking

use std::fs::{self, File};
use std::io::{self, Read, Write, BufReader, BufWriter};
use std::path::{Path, PathBuf};

// 1. File Copy Operations

/// Copy file from source to destination
fn copy_file(source_path: &str, destination_path: &str) -> io::Result<()> {
    // Ensure destination directory exists
    if let Some(parent) = Path::new(destination_path).parent() {
        fs::create_dir_all(parent)?;
    }

    fs::copy(source_path, destination_path)?;
    Ok(())
}

/// Copy file using a buffer
fn copy_file_with_buffer(
    source_path: &str,
    destination_path: &str,
    buffer_size: usize,
) -> io::Result<()> {
    let source_file = File::open(source_path)?;
    let mut reader = BufReader::with_capacity(buffer_size, source_file);

    // Ensure destination directory exists
    if let Some(parent) = Path::new(destination_path).parent() {
        fs::create_dir_all(parent)?;
    }

    let destination_file = File::create(destination_path)?;
    let mut writer = BufWriter::with_capacity(buffer_size, destination_file);

    let mut buffer = vec![0u8; buffer_size];
    loop {
        let n = reader.read(&mut buffer)?;
        if n == 0 {
            break;
        }
        writer.write_all(&buffer[..n])?;
    }

    writer.flush()?;
    Ok(())
}

/// Copy file with progress tracking
type ProgressCallback = fn(written: u64, total: u64);

fn copy_file_with_progress(
    source_path: &str,
    destination_path: &str,
    progress_callback: Option<ProgressCallback>,
) -> io::Result<()> {
    let source_file = File::open(source_path)?;
    let total_size = source_file.metadata()?.len();
    let mut reader = BufReader::new(source_file);

    // Ensure destination directory exists
    if let Some(parent) = Path::new(destination_path).parent() {
        fs::create_dir_all(parent)?;
    }

    let destination_file = File::create(destination_path)?;
    let mut writer = BufWriter::new(destination_file);

    let mut written: u64 = 0;
    const BUFFER_SIZE: usize = 32 * 1024; // 32KB
    let mut buffer = vec![0u8; BUFFER_SIZE];

    loop {
        let n = reader.read(&mut buffer)?;
        if n == 0 {
            break;
        }

        writer.write_all(&buffer[..n])?;
        written += n as u64;

        if let Some(callback) = progress_callback {
            callback(written, total_size);
        }
    }

    writer.flush()?;

    // Copy permissions
    let source_metadata = fs::metadata(source_path)?;
    let destination_metadata = destination_file.metadata()?;
    let mut permissions = destination_metadata.permissions();
    permissions.set_readonly(source_metadata.permissions().readonly());
    fs::set_permissions(destination_path, permissions)?;

    Ok(())
}

// 2. File Move Operations

/// Move file from source to destination
fn move_file(source_path: &str, destination_path: &str) -> io::Result<()> {
    // Try rename first (fastest if on same filesystem)
    if fs::rename(source_path, destination_path).is_ok() {
        return Ok(());
    }

    // If rename fails, copy and delete
    copy_file(source_path, destination_path)?;
    fs::remove_file(source_path)?;
    Ok(())
}

/// Move file atomically
fn move_file_atomic(source_path: &str, destination_path: &str) -> io::Result<()> {
    // Ensure destination directory exists
    if let Some(parent) = Path::new(destination_path).parent() {
        fs::create_dir_all(parent)?;
    }

    fs::rename(source_path, destination_path)
}

// 3. Directory Operations

/// Copy entire directory recursively
fn copy_directory(source_dir: &str, destination_dir: &str) -> io::Result<()> {
    let source_metadata = fs::metadata(source_dir)?;

    // Create destination directory
    fs::create_dir_all(destination_dir)?;
    fs::set_permissions(destination_dir, source_metadata.permissions())?;

    // Read source directory
    let entries = fs::read_dir(source_dir)?;

    for entry in entries {
        let entry = entry?;
        let source_path = entry.path();
        let file_name = source_path.file_name()
            .and_then(|n| n.to_str())
            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid filename"))?;
        let dest_path = Path::new(destination_dir).join(file_name);

        if entry.path().is_dir() {
            // Recursively copy subdirectory
            copy_directory(source_path.to_str()
                .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid path"))?,
                dest_path.to_str()
                .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid path"))?)?;
        } else {
            // Copy file
            copy_file(source_path.to_str()
                .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid path"))?,
                dest_path.to_str()
                .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid path"))?)?;
        }
    }

    Ok(())
}

/// Copy directory with progress tracking
fn copy_directory_with_progress(
    source_dir: &str,
    destination_dir: &str,
    progress_callback: Option<fn(current: usize, total: usize, filename: &str)>,
) -> io::Result<()> {
    // Count total files first
    let mut total_files = 0;
    count_files(source_dir, &mut total_files)?;

    let mut copied_files = 0;

    // Copy files
    copy_directory_recursive(source_dir, destination_dir, &mut copied_files, total_files, progress_callback)
}

fn count_files(dir: &str, count: &mut usize) -> io::Result<()> {
    let entries = fs::read_dir(dir)?;
    for entry in entries {
        let entry = entry?;
        if entry.path().is_dir() {
            let path = entry.path().to_str()
                .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid path"))?;
            count_files(path, count)?;
        } else {
            *count += 1;
        }
    }
    Ok(())
}

fn copy_directory_recursive(
    source_dir: &str,
    destination_dir: &str,
    copied_files: &mut usize,
    total_files: usize,
    progress_callback: Option<fn(usize, usize, &str)>,
) -> io::Result<()> {
    let source_metadata = fs::metadata(source_dir)?;
    fs::create_dir_all(destination_dir)?;
    fs::set_permissions(destination_dir, source_metadata.permissions())?;

    let entries = fs::read_dir(source_dir)?;

    for entry in entries {
        let entry = entry?;
        let source_path = entry.path();
        let file_name = source_path.file_name()
            .and_then(|n| n.to_str())
            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid filename"))?;
        let dest_path = Path::new(destination_dir).join(file_name);

        if source_path.is_dir() {
            copy_directory_recursive(
                source_path.to_str()
                    .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid path"))?,
                dest_path.to_str()
                    .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid path"))?,
                copied_files,
                total_files,
                progress_callback,
            )?;
        } else {
            copy_file(
                source_path.to_str()
                    .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid path"))?,
                dest_path.to_str()
                    .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid path"))?,
            )?;

            *copied_files += 1;
            if let Some(callback) = progress_callback {
                callback(*copied_files, total_files, file_name);
            }
        }
    }

    Ok(())
}

/// Move directory from source to destination
fn move_directory(source_dir: &str, destination_dir: &str) -> io::Result<()> {
    // Try atomic rename first
    if fs::rename(source_dir, destination_dir).is_ok() {
        return Ok(());
    }

    // If rename fails, copy and delete
    copy_directory(source_dir, destination_dir)?;
    fs::remove_dir_all(source_dir)?;
    Ok(())
}

// 4. Batch Operations

/// Rename files in a directory using a pattern
fn batch_rename_files<F>(directory: &str, rename_func: F) -> io::Result<(Vec<String>, Vec<String>)>
where
    F: Fn(&str) -> String,
{
    let mut renamed = Vec::new();
    let mut failed = Vec::new();

    let entries = fs::read_dir(directory)?;

    for entry in entries {
        let entry = entry?;
        if entry.path().is_dir() {
            continue;
        }

        let old_name = entry.file_name().to_string_lossy().to_string();
        let old_path = Path::new(directory).join(&old_name);
        let new_name = rename_func(&old_name);
        let new_path = Path::new(directory).join(&new_name);

        match fs::rename(&old_path, &new_path) {
            Ok(_) => renamed.push(format!("{} -> {}", old_path.display(), new_path.display())),
            Err(_) => failed.push(old_path.to_string_lossy().to_string()),
        }
    }

    Ok((renamed, failed))
}

// 5. Safe File Operations

/// Safe copy with validation
fn safe_copy(source_path: &str, destination_path: &str) -> io::Result<()> {
    // Validate source
    if !Path::new(source_path).exists() {
        return Err(io::Error::new(io::ErrorKind::NotFound, "Source file does not exist"));
    }

    // Check if destination exists
    if Path::new(destination_path).exists() {
        return Err(io::Error::new(io::ErrorKind::AlreadyExists, "Destination file already exists"));
    }

    copy_file(source_path, destination_path)
}

/// Safe move with validation
fn safe_move(source_path: &str, destination_path: &str) -> io::Result<()> {
    // Validate source
    if !Path::new(source_path).exists() {
        return Err(io::Error::new(io::ErrorKind::NotFound, "Source file does not exist"));
    }

    // Check if destination exists
    if Path::new(destination_path).exists() {
        return Err(io::Error::new(io::ErrorKind::AlreadyExists, "Destination file already exists"));
    }

    // Ensure destination directory exists
    if let Some(parent) = Path::new(destination_path).parent() {
        fs::create_dir_all(parent)?;
    }

    move_file(source_path, destination_path)
}

// 6. Backup and Restore

/// Create backup of files/directories
fn create_backup(source_paths: &[String], backup_dir: &str, use_timestamp: bool) -> io::Result<String> {
    let backup_path = if use_timestamp {
        let timestamp = chrono::Local::now().format("%Y%m%d_%H%M%S");
        Path::new(backup_dir).join(format!("backup_{}", timestamp))
    } else {
        Path::new(backup_dir).join("backup")
    };

    fs::create_dir_all(&backup_path)?;

    for source in source_paths {
        let dest = backup_path.join(
            Path::new(source)
                .file_name()
                .and_then(|n| n.to_str())
                .unwrap_or("unknown")
        );

        let metadata = fs::metadata(source)?;
        if metadata.is_dir() {
            copy_directory(source, dest.to_str()
                .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid path"))?)?;
        } else {
            copy_file(source, dest.to_str()
                .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid path"))?)?;
        }
    }

    Ok(backup_path.to_str()
        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid path"))?
        .to_string())
}

/// Restore files from backup directory
fn restore_from_backup(backup_dir: &str, target_dir: &str) -> io::Result<()> {
    copy_directory(backup_dir, target_dir)
}

// Usage Examples
fn main() -> io::Result<()> {
    println!("=== Web Rust File Copy/Move Examples ===\n");

    // 1. Copy file
    println!("--- 1. Copy File ---");
    match copy_file("source.txt", "copy.txt") {
        Ok(_) => println!("File copied successfully"),
        Err(e) => println!("Error: {}", e),
    }

    // 2. Copy with progress
    println!("\n--- 2. Copy with Progress ---");
    match copy_file_with_progress("source.txt", "copy2.txt", Some(|written, total| {
        println!("Progress: {}/{} bytes ({:.1}%)", written, total, (written as f64 / total as f64) * 100.0);
    })) {
        Ok(_) => println!("File copied with progress successfully"),
        Err(e) => println!("Error: {}", e),
    }

    // 3. Move file
    println!("\n--- 3. Move File ---");
    match move_file("old_name.txt", "new_name.txt") {
        Ok(_) => println!("File moved successfully"),
        Err(e) => println!("Error: {}", e),
    }

    // 4. Batch rename
    println!("\n--- 4. Batch Rename ---");
    match batch_rename_files(".", |name| {
        name.replace(".txt", ".bak")
    }) {
        Ok((renamed, failed)) => {
            println!("Renamed {} files", renamed.len());
            println!("Failed {} files", failed.len());
        }
        Err(e) => println!("Error: {}", e),
    }

    // 5. Backup
    println!("\n--- 5. Backup ---");
    match create_backup(&["important_file.txt".to_string(), "important_dir".to_string()], "backups", true) {
        Ok(backup_path) => println!("Backup created at: {}", backup_path),
        Err(e) => println!("Error: {}", e),
    }

    println!("\n=== All File Copy/Move Examples Completed ===");
    Ok(())
}
代码rust复杂

目录遍历

使用WalkDir、glob模式和文件探索遍历目录

难度

7/10

预计时间

30 min

标签

rust, web, file operations, directory

前置条件

Advanced Rust, std::fs, std::path

rust
// Web Rust Directory Traversal Examples
// Using walkdir and glob patterns for directory operations

use std::collections::{HashMap, HashSet};
use std::fs::{self, File};
use std::io;
use std::path::{Path, PathBuf};
use std::ffi::OsStr;

// 1. Directory Listing

/// Represents contents of a directory
#[derive(Debug)]
struct DirectoryContents {
    directory: String,
    files: Vec<String>,
    directories: Vec<String>,
}

/// List contents of a directory (non-recursive)
fn list_directory_contents(directory: &str, recursive: bool) -> io::Result<DirectoryContents> {
    let mut result = DirectoryContents {
        directory: directory.to_string(),
        files: Vec::new(),
        directories: Vec::new(),
    };

    if recursive {
        list_directory_recursive(directory, &mut result)?;
    } else {
        let entries = fs::read_dir(directory)?;
        for entry in entries {
            let entry = entry?;
            let path = entry.path();
            let path_str = path.to_string_lossy().to_string();

            if path.is_dir() {
                result.directories.push(path_str);
            } else {
                result.files.push(path_str);
            }
        }
    }

    Ok(result)
}

fn list_directory_recursive(directory: &str, result: &mut DirectoryContents) -> io::Result<()> {
    let entries = fs::read_dir(directory)?;
    for entry in entries {
        let entry = entry?;
        let path = entry.path();
        let path_str = path.to_string_lossy().to_string();

        if path.is_dir() && path_str != result.directory {
            result.directories.push(path_str.clone());
            list_directory_recursive(&path_str, result)?;
        } else if !path.is_dir() {
            result.files.push(path_str);
        }
    }
    Ok(())
}

/// TreeNode represents a node in the directory tree
#[derive(Debug)]
struct TreeNode {
    name: String,
    path: String,
    is_dir: bool,
    children: Vec<TreeNode>,
}

/// List directory as a tree structure
fn list_directory_tree(directory: &str, max_depth: usize) -> io::Result<TreeNode> {
    build_tree(directory, 0, max_depth)
}

fn build_tree(path: &str, depth: usize, max_depth: usize) -> io::Result<TreeNode> {
    let path_obj = Path::new(path);
    let metadata = fs::metadata(path)?;

    let mut node = TreeNode {
        name: path_obj.file_name()
            .and_then(|n| n.to_str())
            .unwrap_or("")
            .to_string(),
        path: path.to_string(),
        is_dir: metadata.is_dir(),
        children: Vec::new(),
    };

    if metadata.is_dir() && (max_depth == 0 || depth < max_depth) {
        let mut entries: Vec<_> = fs::read_dir(path)?
            .filter_map(|e| e.ok())
            .collect();

        // Sort by name
        entries.sort_by_key(|e| e.file_name());

        for entry in entries {
            let child_path = entry.path().to_string_lossy().to_string();
            match build_tree(&child_path, depth + 1, max_depth) {
                Ok(child) => node.children.push(child),
                Err(_) => continue,
            }
        }
    }

    Ok(node)
}

/// Print directory tree structure
fn print_directory_tree(tree: &TreeNode, indent: usize) {
    let prefix = "  ".repeat(indent);
    let icon = if tree.is_dir { "📁" } else { "📄" };
    println!("{}{} {}", prefix, icon, tree.name);

    for child in &tree.children {
        print_directory_tree(child, indent + 1);
    }
}

// 2. File Pattern Matching

/// Find files matching a pattern
fn find_files_by_pattern(directory: &str, pattern: &str) -> io::Result<Vec<String>> {
    let mut matches = Vec::new();

    fn walk(dir: &Path, pattern: &str, matches: &mut Vec<String>) -> io::Result<()> {
        let entries = fs::read_dir(dir)?;
        for entry in entries {
            let entry = entry?;
            let path = entry.path();

            if path.is_dir() {
                walk(&path, pattern, matches)?;
            } else {
                let file_name = path.file_name()
                    .and_then(|n| n.to_str())
                    .unwrap_or("");

                if pattern_matches(pattern, file_name) {
                    matches.push(path.to_string_lossy().to_string());
                }
            }
        }
        Ok(())
    }

    walk(Path::new(directory), pattern, &mut matches)?;
    Ok(matches)
}

/// Simple pattern matching (supports * and ?)
fn pattern_matches(pattern: &str, text: &str) -> bool {
    let pattern_chars: Vec<char> = pattern.chars().collect();
    let text_chars: Vec<char> = text.chars().collect();

    fn match_helper(p: &[char], t: &[char]) -> bool {
        if p.is_empty() {
            return t.is_empty();
        }

        if p[0] == '*' {
            // Try matching * with 0 or more characters
            for i in 0..=t.len() {
                if match_helper(&p[1..], &t[i..]) {
                    return true;
                }
            }
            return false;
        }

        if t.is_empty() {
            return false;
        }

        if p[0] == '?' || p[0] == t[0] {
            return match_helper(&p[1..], &t[1..]);
        }

        false
    }

    match_helper(&pattern_chars, &text_chars)
}

/// Find files with specific extension
fn find_files_by_extension(directory: &str, extension: &str) -> io::Result<Vec<String>> {
    let ext = if extension.starts_with('.') {
        extension.to_string()
    } else {
        format!(".{}", extension)
    };
    find_files_by_pattern(directory, &format!("*{}", ext))
}

/// Find files by name pattern
fn find_files_by_name(directory: &str, name_pattern: &str) -> io::Result<Vec<String>> {
    find_files_by_pattern(directory, name_pattern)
}

// 3. Directory Statistics

/// Represents statistics about a directory
#[derive(Debug)]
struct DirectoryStats {
    total_files: u64,
    total_directories: u64,
    total_size: u64,
    extension_counts: HashMap<String, usize>,
    largest_files: Vec<FileEntry>,
}

/// Represents a file entry with size
#[derive(Debug, Clone)]
struct FileEntry {
    path: String,
    size: u64,
}

/// Get statistics about a directory
fn get_directory_statistics(directory: &str) -> io::Result<DirectoryStats> {
    let mut stats = DirectoryStats {
        total_files: 0,
        total_directories: 0,
        total_size: 0,
        extension_counts: HashMap::new(),
        largest_files: Vec::new(),
    };

    fn walk(dir: &Path, stats: &mut DirectoryStats) -> io::Result<()> {
        let entries = fs::read_dir(dir)?;
        for entry in entries {
            let entry = entry?;
            let path = entry.path();
            let metadata = entry.metadata()?;

            if metadata.is_dir() {
                stats.total_directories += 1;
                walk(&path, stats)?;
            } else {
                stats.total_files += 1;
                stats.total_size += metadata.len();

                // Track extensions
                if let Some(ext) = path.extension() {
                    let ext_str = ext.to_string_lossy().to_lowercase();
                    *stats.extension_counts.entry(ext_str).or_insert(0) += 1;
                }

                // Track largest files
                stats.largest_files.push(FileEntry {
                    path: path.to_string_lossy().to_string(),
                    size: metadata.len(),
                });
            }
        }
        Ok(())
    }

    walk(Path::new(directory), &mut stats)?;

    // Sort largest files and keep top 10
    stats.largest_files.sort_by(|a, b| b.size.cmp(&a.size));
    stats.largest_files.truncate(10);

    Ok(stats)
}

// 4. File Search in Directory

/// Search for text in files within a directory
fn search_in_directory(
    directory: &str,
    search_text: &str,
    file_pattern: &str,
) -> io::Result<HashMap<String, Vec<usize>>> {
    let mut results = HashMap::new();

    let files = find_files_by_pattern(directory, file_pattern)?;
    for file_path in files {
        match search_in_file(&file_path, search_text) {
            Ok(matching_lines) if !matching_lines.is_empty() => {
                results.insert(file_path, matching_lines);
            }
            _ => {}
        }
    }

    Ok(results)
}

fn search_in_file(file_path: &str, search_text: &str) -> io::Result<Vec<usize>> {
    let content = fs::read_to_string(file_path)?;
    let lines: Vec<&str> = content.lines().collect();

    let mut matching_lines = Vec::new();
    for (i, line) in lines.iter().enumerate() {
        if line.contains(search_text) {
            matching_lines.push(i + 1);
        }
    }

    Ok(matching_lines)
}

// 5. Directory Comparison

/// Represents comparison results
#[derive(Debug)]
struct DirectoryComparison {
    only_in_dir1: Vec<String>,
    only_in_dir2: Vec<String>,
    different: Vec<String>,
    same: Vec<String>,
}

/// Compare two directories and find differences
fn compare_directories(dir1: &str, dir2: &str) -> io::Result<DirectoryComparison> {
    let mut result = DirectoryComparison {
        only_in_dir1: Vec::new(),
        only_in_dir2: Vec::new(),
        different: Vec::new(),
        same: Vec::new(),
    };

    let mut files1: HashMap<String, u64> = HashMap::new();
    let mut files2: HashMap<String, u64> = HashMap::new();

    // Walk dir1
    walk_files(Path::new(dir1), dir1, &mut files1)?;

    // Walk dir2
    walk_files(Path::new(dir2), dir2, &mut files2)?;

    // Find files only in dir1
    for path in files1.keys() {
        if !files2.contains_key(path) {
            result.only_in_dir1.push(path.clone());
        }
    }

    // Find files only in dir2
    for path in files2.keys() {
        if !files1.contains_key(path) {
            result.only_in_dir2.push(path.clone());
        }
    }

    // Compare common files
    for (path, size1) in &files1 {
        if let Some(size2) = files2.get(path) {
            if size1 == size2 {
                result.same.push(path.clone());
            } else {
                result.different.push(path.clone());
            }
        }
    }

    Ok(result)
}

fn walk_files(dir: &Path, base: &str, files: &mut HashMap<String, u64>) -> io::Result<()> {
    let entries = fs::read_dir(dir)?;
    for entry in entries {
        let entry = entry?;
        let path = entry.path();

        if path.is_dir() {
            walk_files(&path, base, files)?;
        } else {
            let rel_path = path.strip_prefix(base)
                .unwrap_or(&path)
                .to_string_lossy()
                .replace("\\", "/");
            let size = entry.metadata()?.len();
            files.insert(rel_path, size);
        }
    }
    Ok(())
}

// 6. Directory Cleanup

/// Cleanup directory by removing files matching patterns
fn cleanup_directory(directory: &str, dry_run: bool, patterns: Option<Vec<String>>) -> (Vec<String>, Vec<String>) {
    let mut deleted = Vec::new();
    let mut failed = Vec::new();

    let patterns = patterns.unwrap_or_else(|| vec![
        "*.tmp".to_string(),
        "*.log".to_string(),
        "*.bak".to_string(),
        "*~".to_string(),
    ]);

    let entries = match fs::read_dir(directory) {
        Ok(e) => e,
        Err(_) => return (deleted, failed),
    };

    for entry in entries.filter_map(|e| e.ok()) {
        if entry.path().is_dir() {
            continue;
        }

        let file_name = entry.file_name().to_string_lossy().to_string();
        let full_path = entry.path();

        // Check if matches any pattern
        let matches = patterns.iter().any(|pattern| {
            pattern_matches(pattern, &file_name)
        });

        if matches {
            if dry_run {
                println!("Would delete: {}", full_path.display());
                deleted.push(full_path.to_string_lossy().to_string());
            } else {
                match fs::remove_file(&full_path) {
                    Ok(_) => deleted.push(full_path.to_string_lossy().to_string()),
                    Err(_) => failed.push(full_path.to_string_lossy().to_string()),
                }
            }
        }
    }

    (deleted, failed)
}

/// Remove empty directories
fn remove_empty_directories(directory: &str, recursive: bool) -> usize {
    let mut removed_count = 0;

    if recursive {
        remove_empty_recursive(directory, &mut removed_count);
    }

    // Check and remove the root directory if it's empty
    if let Ok(mut entries) = fs::read_dir(directory) {
        if entries.next().is_none() {
            if fs::remove_dir(directory).is_ok() {
                removed_count += 1;
                println!("Removed empty directory: {}", directory);
            }
        }
    }

    removed_count
}

fn remove_empty_recursive(dir: &str, count: &mut usize) {
    if let Ok(entries) = fs::read_dir(dir) {
        for entry in entries.filter_map(|e| e.ok()) {
            if entry.path().is_dir() {
                let path = entry.path().to_string_lossy().to_string();
                remove_empty_recursive(&path, count);
            }
        }
    }

    // Check if directory is now empty
    if let Ok(mut entries) = fs::read_dir(dir) {
        if entries.next().is_none() {
            if fs::remove_dir(dir).is_ok() {
                *count += 1;
                println!("Removed empty directory: {}", dir);
            }
        }
    }
}

// Usage Examples
fn main() -> io::Result<()> {
    println!("=== Web Rust Directory Traversal Examples ===\n");

    // 1. List directory contents
    println!("--- 1. List Directory Contents ---");
    match list_directory_contents(".", false) {
        Ok(contents) => {
            println!("Files: {}", contents.files.len());
            println!("Directories: {}", contents.directories.len());
        }
        Err(e) => println!("Error: {}", e),
    }

    // 2. Directory tree
    println!("\n--- 2. Directory Tree ---");
    match list_directory_tree(".", 2) {
        Ok(tree) => print_directory_tree(&tree, 0),
        Err(e) => println!("Error: {}", e),
    }

    // 3. Find files by pattern
    println!("\n--- 3. Find Files by Pattern ---");
    match find_files_by_extension(".", "txt") {
        Ok(txt_files) => println!("Text files: {}", txt_files.len()),
        Err(e) => println!("Error: {}", e),
    }

    // 4. Directory statistics
    println!("\n--- 4. Directory Statistics ---");
    match get_directory_statistics(".") {
        Ok(stats) => {
            println!("Total files: {}", stats.total_files);
            println!("Total size: {}", format_file_size(stats.total_size));
            println!("Extensions: {:?}", stats.extension_counts);
        }
        Err(e) => println!("Error: {}", e),
    }

    // 5. Search in directory
    println!("\n--- 5. Search in Directory ---");
    match search_in_directory(".", "TODO", "*.txt") {
        Ok(results) => println!("Files containing 'TODO': {}", results.len()),
        Err(e) => println!("Error: {}", e),
    }

    // 6. Compare directories
    println!("\n--- 6. Compare Directories ---");
    match compare_directories("dir1", "dir2") {
        Ok(comparison) => {
            println!("Only in dir1: {}", comparison.only_in_dir1.len());
            println!("Only in dir2: {}", comparison.only_in_dir2.len());
            println!("Different: {}", comparison.different.len());
        }
        Err(e) => println!("Error: {}", e),
    }

    // 7. Cleanup
    println!("\n--- 7. Cleanup ---");
    let (deleted, _) = cleanup_directory(".", true, Some(vec!["*.tmp".to_string()]));
    println!("Would delete {} files", deleted.len());

    println!("\n=== All Directory Traversal Examples Completed ===");
    Ok(())
}

fn format_file_size(bytes: u64) -> String {
    const KB: u64 = 1024;
    const MB: u64 = KB * 1024;
    const GB: u64 = MB * 1024;

    if bytes >= GB {
        format!("{:.2} GB", bytes as f64 / GB as f64)
    } else if bytes >= MB {
        format!("{:.2} MB", bytes as f64 / MB as f64)
    } else if bytes >= KB {
        format!("{:.2} KB", bytes as f64 / KB as f64)
    } else {
        format!("{} B", bytes)
    }
}

工具

经常与该案例搭配使用的工具

相关内容

相关案例集合

工具使用指南

帮助搜索与理解工作流的案例说明

标题

Web Rust 文件操作示例

描述

Web Rust 文件操作示例,包括文本文件读写、文件复制移动、目录遍历和文件验证

概览

Web Rust 文件操作示例,包括文本文件读写、文件复制移动、目录遍历和文件验证

适用场景

  • Use this sample set when you want reference inputs for Web Rust 文件操作示例.
  • Use it to preview output formats and compare related sample variants.

使用方式

  1. 1Open a sample entry to inspect the prepared example payload or asset.
  2. 2Compare it with related tools to understand how the workflow connects.

常见用途

  • 文本文件读写
  • 文件复制/移动
  • 目录遍历

常见问题

What is included in Web Rust 文件操作示例?

This page includes curated sample entries, related tools, and neighboring sample collections for Web Rust 文件操作示例.

首页
工具3197
案例593
可视化461
文件查看器103
专题229
标签333
图像、音频与视频655

图像、音频、视频处理、转换与优化工具

数学、日期与金融644

计算器、数值计算、日期逻辑、统计与金融工具

设计与颜色296

颜色、版式、图形、视觉样式与设计辅助工具

文本与写作209

写作、清洗、格式化、提取与文本分析工具

开发与 Web193

开发者工具、网络处理、Web 调试与自动化辅助工具

转换与编码175

格式、文件、压缩包、单位与编码转换工具

界面截图结构化、人工校对与开发交付
钢琴旋律草稿创作、MIDI 核验与试听样稿交付
复古游戏纹理制作、候选评审与素材交付
操作截图到可复核图文指南与培训交付
网页改版响应式视觉回归审核与证据交付

© 2026 Elysia Tools. 保留所有权利。

文档价格关于我们服务条款隐私政策Cookie 政策联系我们

Media

MOV转MP4转换器

将MOV视频文件转换为MP4格式

Media

MP4转MOV转换器

将MP4视频文件转换为MOV格式

Media

智能媒体重封装

使用流复制快速更换媒体容器。

Media

批量视频转换器

批量转换多个视频文件格式,支持 MP4、WebM、MKV、AVI、MOV、FLV,支持质量设置和批量处理

Media

视频转GIF优化版

将视频转换为优化后的GIF格式,支持高级压缩设置

Media

视频剪辑器

精确剪辑视频片段,支持多种格式和质量设置

Media

视频横向翻转

将视频左右镜像翻转,保持原容器格式并尽量直接复制音轨

Media

视频音视频流提取

先检测流信息,再以无转码方式直接提取音频流、无音频视频流或同时提取两者

Go

Web Go 文件操作示例

Web Go 文件操作示例,包括文本文件读写、文件复制移动、目录遍历和文件验证

Python

Web Python 文件操作示例

Web Python 文件操作示例,包括文本文件读写、文件复制移动、目录遍历和文件验证

TypeScript

Web TypeScript 文件操作示例

Web TypeScript 文件操作示例,包括文本文件读写、文件复制移动、目录遍历和文件验证

Java

Android Java 文件操作示例

Android Java 文件操作示例,包括文本文件读写、文件复制移动、目录遍历和文件验证

Kotlin

Android Kotlin 文件操作示例

Android Kotlin 文件操作示例,包括文本文件读写、文件复制移动、目录遍历和文件验证

Objective-C

macOS Objective-C 文件操作示例

macOS Objective-C 文件操作示例,包括文本文件读写、文件复制移动、目录遍历和文件验证