Exemples d'Opérations sur Fichiers Web Go

Exemples d'opérations sur fichiers Web Go incluant lecture/écriture de fichiers texte, copier/déplacer, parcours de répertoires et validation de fichiers

💻 Lecture/Écriture de Fichiers Texte go

🟢 simple ⭐⭐

Lire et écrire des fichiers texte avec diverses options de codification en utilisant Go file I/O

⏱️ 20 min 🏷️ go, web, file operations
Prerequisites: Basic Go, File I/O, os package
// Web Go Text File Read/Write Examples
// Using Go file I/O for file operations

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"unicode/utf8"
)

// 1. Reading Text Files

// ReadTextFile reads a text file and returns its content
func ReadTextFile(filePath string) (string, error) {
	content, err := ioutil.ReadFile(filePath)
	if err != nil {
		return "", fmt.Errorf("error reading file: %v", err)
	}
	return string(content), nil
}

// ReadTextFileWithEncoding reads a text file with specific encoding
func ReadTextFileWithEncoding(filePath string, encoding string) (string, error) {
	// Go primarily works with UTF-8, but we can read and validate
	content, err := ioutil.ReadFile(filePath)
	if err != nil {
		return "", fmt.Errorf("error reading file: %v", err)
	}

	// Validate UTF-8
	if !utf8.Valid(content) {
		return "", fmt.Errorf("file contains invalid UTF-8 sequences")
	}

	return string(content), nil
}

// ReadTextFileLineByLine reads a text file line by line
func ReadTextFileLineByLine(filePath string) ([]string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, fmt.Errorf("error opening file: %v", err)
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}

	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("error reading file: %v", err)
	}

	return lines, nil
}

// ReadTextFileInChunks reads a text file in chunks
func ReadTextFileInChunks(filePath string, chunkSize int) ([]string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, fmt.Errorf("error opening file: %v", err)
	}
	defer file.Close()

	var chunks []string
	buffer := make([]byte, chunkSize)

	for {
		n, err := file.Read(buffer)
		if err != nil && err != io.EOF {
			return nil, fmt.Errorf("error reading file: %v", err)
		}

		if n == 0 {
			break
		}

		chunks = append(chunks, string(buffer[:n]))
	}

	return chunks, nil
}

// ReadTextFileWithBuffer reads a text file using buffered I/O
func ReadTextFileWithBuffer(filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", fmt.Errorf("error opening file: %v", err)
	}
	defer file.Close()

	var buffer bytes.Buffer
	_, err = io.Copy(&buffer, file)
	if err != nil {
		return "", fmt.Errorf("error reading file: %v", err)
	}

	return buffer.String(), nil
}

// 2. Writing Text Files

// WriteTextFile writes text content to a file
func WriteTextFile(filePath string, content string) error {
	err := ioutil.WriteFile(filePath, []byte(content), 0644)
	if err != nil {
		return fmt.Errorf("error writing file: %v", err)
	}
	return nil
}

// WriteTextLines writes a list of lines to a text file
func WriteTextLines(filePath string, lines []string) error {
	file, err := os.Create(filePath)
	if err != nil {
		return fmt.Errorf("error creating file: %v", err)
	}
	defer file.Close()

	writer := bufio.NewWriter(file)
	for _, line := range lines {
		_, err := writer.WriteString(line + "\n")
		if err != nil {
			return fmt.Errorf("error writing to file: %v", err)
		}
	}

	return writer.Flush()
}

// WriteTextFileWithPermissions writes text content with specific permissions
func WriteTextFileWithPermissions(filePath string, content string, perm os.FileMode) error {
	err := ioutil.WriteFile(filePath, []byte(content), perm)
	if err != nil {
		return fmt.Errorf("error writing file: %v", err)
	}
	return nil
}

// AppendToTextFile appends text content to an existing file
func AppendToTextFile(filePath string, content string) error {
	file, err := os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		return fmt.Errorf("error opening file: %v", err)
	}
	defer file.Close()

	_, err = file.WriteString(content)
	if err != nil {
		return fmt.Errorf("error appending to file: %v", err)
	}

	return nil
}

// WriteTextFileAtomic writes content atomically (using temp file)
func WriteTextFileAtomic(filePath string, content string) error {
	// Write to temporary file
	tempPath := filePath + ".tmp"
	err := ioutil.WriteFile(tempPath, []byte(content), 0644)
	if err != nil {
		return fmt.Errorf("error writing temp file: %v", err)
	}

	// Rename atomically
	err = os.Rename(tempPath, filePath)
	if err != nil {
		os.Remove(tempPath) // Cleanup
		return fmt.Errorf("error renaming file: %v", err)
	}

	return nil
}

// 3. File Info Helper

// GetFileSize gets the size of a file in bytes
func GetFileSize(filePath string) (int64, error) {
	info, err := os.Stat(filePath)
	if err != nil {
		return 0, fmt.Errorf("error getting file info: %v", err)
	}
	return info.Size(), nil
}

// GetFileExtension gets the file extension
func GetFileExtension(filePath string) string {
	return filepath.Ext(filePath)
}

// GetFileNameWithoutExtension gets the file name without extension
func GetFileNameWithoutExtension(filePath string) string {
	return strings.TrimSuffix(filepath.Base(filePath), filepath.Ext(filePath))
}

// FormatFileSize formats file size in human-readable format
func FormatFileSize(bytes int64) string {
	const (
		KB = 1024
		MB = KB * 1024
		GB = MB * 1024
		TB = GB * 1024
	)

	switch {
	case bytes >= TB:
		return fmt.Sprintf("%.2f TB", float64(bytes)/float64(TB))
	case bytes >= GB:
		return fmt.Sprintf("%.2f GB", float64(bytes)/float64(GB))
	case bytes >= MB:
		return fmt.Sprintf("%.2f MB", float64(bytes)/float64(MB))
	case bytes >= KB:
		return fmt.Sprintf("%.2f KB", float64(bytes)/float64(KB))
	default:
		return fmt.Sprintf("%d B", bytes)
	}
}

// GetFileDetails gets detailed information about a file
func GetFileDetails(filePath string) (map[string]interface{}, error) {
	info, err := os.Stat(filePath)
	if err != nil {
		return nil, fmt.Errorf("error getting file info: %v", err)
	}

	absPath, err := filepath.Abs(filePath)
	if err != nil {
		return nil, fmt.Errorf("error getting absolute path: %v", err)
	}

	return map[string]interface{}{
		"name":            filepath.Base(filePath),
		"size":            info.Size(),
		"readable_size":   FormatFileSize(info.Size()),
		"extension":       GetFileExtension(filePath),
		"modified_time":   info.ModTime(),
		"is_file":         !info.IsDir(),
		"is_dir":          info.IsDir(),
		"absolute_path":   absPath,
		"mode":            info.Mode(),
	}, nil
}

// 4. File Validation

// FileExists checks if a file exists
func FileExists(filePath string) bool {
	_, err := os.Stat(filePath)
	return !os.IsNotExist(err)
}

// IsFile checks if path is a file
func IsFile(filePath string) bool {
	info, err := os.Stat(filePath)
	if err != nil {
		return false
	}
	return !info.IsDir()
}

// IsDirectory checks if path is a directory
func IsDirectory(dirPath string) bool {
	info, err := os.Stat(dirPath)
	if err != nil {
		return false
	}
	return info.IsDir()
}

// ValidateFile validates file properties
func ValidateFile(filePath string, maxSizeMB int64) (map[string]interface{}, error) {
	result := make(map[string]interface{})
	result["valid"] = true
	result["errors"] = []string{}

	if !FileExists(filePath) {
		result["valid"] = false
		result["errors"] = append(result["errors"].([]string), "File does not exist")
		return result, nil
	}

	if !IsFile(filePath) {
		result["valid"] = false
		result["errors"] = append(result["errors"].([]string), "Path is not a file")
		return result, nil
	}

	if maxSizeMB > 0 {
		size, err := GetFileSize(filePath)
		if err != nil {
			result["valid"] = false
			result["errors"] = append(result["errors"].([]string), fmt.Sprintf("Error getting file size: %v", err))
			return result, nil
		}

		sizeMB := size / (1024 * 1024)
		if sizeMB > maxSizeMB {
			result["valid"] = false
			result["errors"] = append(result["errors"].([]string), fmt.Sprintf("File size (%.2fMB) exceeds limit (%dMB)", float64(sizeMB), maxSizeMB))
		}
	}

	return result, nil
}

// 5. Batch File Operations

// ReadMultipleFiles reads multiple text files
func ReadMultipleFiles(filePaths []string) map[string]string {
	results := make(map[string]string)

	for _, filePath := range filePaths {
		content, err := ReadTextFile(filePath)
		if err == nil {
			results[filePath] = content
		}
	}

	return results
}

// BatchWriteFiles writes to multiple files
func BatchWriteFiles(fileDataMap map[string]string) ([]string, []string) {
	var successful []string
	var failed []string

	for filePath, content := range fileDataMap {
		err := WriteTextFile(filePath, content)
		if err == nil {
			successful = append(successful, filePath)
		} else {
			failed = append(failed, filePath)
		}
	}

	return successful, failed
}

// SearchInFiles searches for text in multiple files
func SearchInFiles(filePaths []string, searchText string, caseSensitive bool) map[string][]int {
	results := make(map[string][]int)

	if !caseSensitive {
		searchText = strings.ToLower(searchText)
	}

	for _, filePath := range filePaths {
		lines, err := ReadTextFileLineByLine(filePath)
		if err != nil {
			continue
		}

		var matchingLines []int
		for i, line := range lines {
			searchLine := line
			if !caseSensitive {
				searchLine = strings.ToLower(line)
			}

			if strings.Contains(searchLine, searchText) {
				matchingLines = append(matchingLines, i+1)
			}
		}

		if len(matchingLines) > 0 {
			results[filePath] = matchingLines
		}
	}

	return results
}

// 6. File Encoding Utilities

// DetectFileEncoding detects file encoding (simplified)
func DetectFileEncoding(filePath string) (string, error) {
	content, err := ioutil.ReadFile(filePath)
	if err != nil {
		return "", err
	}

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

	// Validate UTF-8
	if utf8.Valid(content) {
		return "UTF-8", nil
	}

	// Default to ASCII/unknown
	return "ASCII/Unknown", nil
}

// ConvertFileEncoding converts file from one encoding to another
func ConvertFileEncoding(inputPath, outputPath string, fromEncoding, toEncoding string) error {
	// Read input file
	content, err := ioutil.ReadFile(inputPath)
	if err != nil {
		return fmt.Errorf("error reading input file: %v", err)
	}

	// Write to output file
	err = ioutil.WriteFile(outputPath, content, 0644)
	if err != nil {
		return fmt.Errorf("error writing output file: %v", err)
	}

	return nil
}

// Usage Examples
func main() {
	fmt.Println("=== Web Go Text File Read/Write Examples ===\n")

	// 1. Read text file
	fmt.Println("--- 1. Read Text File ---")
	content, err := ReadTextFile("example.txt")
	if err == nil {
		fmt.Printf("Content length: %d characters\n", len(content))
	}

	// 2. Read line by line
	fmt.Println("\n--- 2. Read Line by Line ---")
	lines, err := ReadTextFileLineByLine("example.txt")
	if err == nil {
		fmt.Printf("Number of lines: %d\n", len(lines))
		fmt.Printf("First 3 lines: %v\n", lines[:min(3, len(lines))])
	}

	// 3. Write text file
	fmt.Println("\n--- 3. Write Text File ---")
	err = WriteTextFile("output.txt", "Hello, World!\nThis is a new file.")
	if err == nil {
		fmt.Println("File written successfully")
	}

	// 4. Write lines
	fmt.Println("\n--- 4. Write Lines ---")
	linesToWrite := []string{"Line 1", "Line 2", "Line 3"}
	err = WriteTextLines("lines.txt", linesToWrite)
	if err == nil {
		fmt.Println("Lines file written successfully")
	}

	// 5. Get file details
	fmt.Println("\n--- 5. File Details ---")
	details, err := GetFileDetails("example.txt")
	if err == nil {
		fmt.Printf("Name: %v\n", details["name"])
		fmt.Printf("Size: %v\n", details["readable_size"])
		fmt.Printf("Extension: %v\n", details["extension"])
	}

	// 6. File validation
	fmt.Println("\n--- 6. File Validation ---")
	validation, err := ValidateFile("example.txt", 1)
	if err == nil {
		fmt.Printf("Valid: %v\n", validation["valid"])
	}

	fmt.Println("\n=== All Text File Operations Completed ===")
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

💻 Copier/Déplacer des Fichiers go

🟡 intermediate ⭐⭐⭐

Copier et déplacer des fichiers avec suivi de progression et gestion des erreurs

⏱️ 25 min 🏷️ go, web, file operations
Prerequisites: Intermediate Go, io package, filepath package
// Web Go File Copy/Move Examples
// File copy and move operations with progress tracking

package main

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"
)

// 1. File Copy Operations

// CopyFile copies a file from source to destination
func CopyFile(sourcePath, destinationPath string) error {
	sourceFile, err := os.Open(sourcePath)
	if err != nil {
		return fmt.Errorf("error opening source file: %v", err)
	}
	defer sourceFile.Close()

	// Ensure destination directory exists
	destDir := filepath.Dir(destinationPath)
	if err := os.MkdirAll(destDir, 0755); err != nil {
		return fmt.Errorf("error creating destination directory: %v", err)
	}

	destFile, err := os.Create(destinationPath)
	if err != nil {
		return fmt.Errorf("error creating destination file: %v", err)
	}
	defer destFile.Close()

	// Copy contents
	_, err = io.Copy(destFile, sourceFile)
	if err != nil {
		return fmt.Errorf("error copying file: %v", err)
	}

	// Copy file permissions
	sourceInfo, err := os.Stat(sourcePath)
	if err != nil {
		return fmt.Errorf("error getting source file info: %v", err)
	}

	err = os.Chmod(destinationPath, sourceInfo.Mode())
	if err != nil {
		return fmt.Errorf("error setting file permissions: %v", err)
	}

	return nil
}

// CopyFileWithBuffer copies a file using a buffer
func CopyFileWithBuffer(sourcePath, destinationPath string, bufferSize int) error {
	sourceFile, err := os.Open(sourcePath)
	if err != nil {
		return fmt.Errorf("error opening source file: %v", err)
	}
	defer sourceFile.Close()

	destDir := filepath.Dir(destinationPath)
	if err := os.MkdirAll(destDir, 0755); err != nil {
		return fmt.Errorf("error creating destination directory: %v", err)
	}

	destFile, err := os.Create(destinationPath)
	if err != nil {
		return fmt.Errorf("error creating destination file: %v", err)
	}
	defer destFile.Close()

	buffer := make([]byte, bufferSize)
	_, err = io.CopyBuffer(destFile, sourceFile, buffer)
	if err != nil {
		return fmt.Errorf("error copying file: %v", err)
	}

	sourceInfo, err := os.Stat(sourcePath)
	if err != nil {
		return fmt.Errorf("error getting source file info: %v", err)
	}

	return os.Chmod(destinationPath, sourceInfo.Mode())
}

// CopyFileWithProgress copies a file with progress tracking
func CopyFileWithProgress(sourcePath, destinationPath string, progressCallback func(written, total int64)) error {
	sourceFile, err := os.Open(sourcePath)
	if err != nil {
		return fmt.Errorf("error opening source file: %v", err)
	}
	defer sourceFile.Close()

	sourceInfo, err := sourceFile.Stat()
	if err != nil {
		return fmt.Errorf("error getting source file info: %v", err)
	}

	destDir := filepath.Dir(destinationPath)
	if err := os.MkdirAll(destDir, 0755); err != nil {
		return fmt.Errorf("error creating destination directory: %v", err)
	}

	destFile, err := os.Create(destinationPath)
	if err != nil {
		return fmt.Errorf("error creating destination file: %v", err)
	}
	defer destFile.Close()

	// Copy with progress
	buffer := make([]byte, 32*1024) // 32KB buffer
	var written int64

	for {
		n, err := sourceFile.Read(buffer)
		if n > 0 {
			w, writeErr := destFile.Write(buffer[:n])
			if writeErr != nil {
				return fmt.Errorf("error writing to destination: %v", writeErr)
			}
			written += int64(w)

			if progressCallback != nil {
				progressCallback(written, sourceInfo.Size())
			}
		}

		if err != nil {
			if err == io.EOF {
				break
			}
			return fmt.Errorf("error reading source: %v", err)
		}
	}

	// Copy permissions
	return os.Chmod(destinationPath, sourceInfo.Mode())
}

// 2. File Move Operations

// MoveFile moves a file from source to destination
func MoveFile(sourcePath, destinationPath string) error {
	// Try rename first (fastest if on same filesystem)
	err := os.Rename(sourcePath, destinationPath)
	if err == nil {
		return nil
	}

	// If rename fails, copy and delete
	if err := CopyFile(sourcePath, destinationPath); err != nil {
		return fmt.Errorf("error copying file: %v", err)
	}

	// Remove source file
	return os.Remove(sourcePath)
}

// MoveFileAtomic moves a file atomically
func MoveFileAtomic(sourcePath, destinationPath string) error {
	// Ensure destination directory exists
	destDir := filepath.Dir(destinationPath)
	if err := os.MkdirAll(destDir, 0755); err != nil {
		return fmt.Errorf("error creating destination directory: %v", err)
	}

	// Atomic rename
	return os.Rename(sourcePath, destinationPath)
}

// 3. Directory Operations

// CopyDirectory copies entire directory recursively
func CopyDirectory(sourceDir, destinationDir string) error {
	sourceInfo, err := os.Stat(sourceDir)
	if err != nil {
		return fmt.Errorf("error getting source directory info: %v", err)
	}

	// Create destination directory
	if err := os.MkdirAll(destinationDir, sourceInfo.Mode()); err != nil {
		return fmt.Errorf("error creating destination directory: %v", err)
	}

	// Read source directory
	entries, err := os.ReadDir(sourceDir)
	if err != nil {
		return fmt.Errorf("error reading source directory: %v", err)
	}

	// Copy each entry
	for _, entry := range entries {
		sourcePath := filepath.Join(sourceDir, entry.Name())
		destPath := filepath.Join(destinationDir, entry.Name())

		if entry.IsDir() {
			// Recursively copy subdirectory
			if err := CopyDirectory(sourcePath, destPath); err != nil {
				return err
			}
		} else {
			// Copy file
			if err := CopyFile(sourcePath, destPath); err != nil {
				return err
			}
		}
	}

	return nil
}

// CopyDirectoryWithProgress copies directory with progress tracking
func CopyDirectoryWithProgress(sourceDir, destinationDir string, progressCallback func(current, total int, filename string)) error {
	// Count total files first
	totalFiles := 0
	filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error {
		if !info.IsDir() {
			totalFiles++
		}
		return nil
	})

	copiedFiles := 0

	// Copy files
	return filepath.Walk(sourceDir, func(sourcePath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		// Calculate destination path
		relPath, err := filepath.Rel(sourceDir, sourcePath)
		if err != nil {
			return err
		}
		destPath := filepath.Join(destinationDir, relPath)

		// Ensure destination directory exists
		destDir := filepath.Dir(destPath)
		if err := os.MkdirAll(destDir, 0755); err != nil {
			return err
		}

		// Copy file
		if err := CopyFile(sourcePath, destPath); err != nil {
			return err
		}

		copiedFiles++
		if progressCallback != nil {
			progressCallback(copiedFiles, totalFiles, filepath.Base(sourcePath))
		}

		return nil
	})
}

// MoveDirectory moves directory from source to destination
func MoveDirectory(sourceDir, destinationDir string) error {
	// Try atomic rename first
	err := os.Rename(sourceDir, destinationDir)
	if err == nil {
		return nil
	}

	// If rename fails, copy and delete
	if err := CopyDirectory(sourceDir, destinationDir); err != nil {
		return fmt.Errorf("error copying directory: %v", err)
	}

	return os.RemoveAll(sourceDir)
}

// 4. Batch Operations

// BatchRenameFiles renames files in a directory using a pattern
func BatchRenameFiles(directory string, renameFunc func(string) string) ([]string, []string) {
	var renamed []string
	var failed []string

	entries, err := os.ReadDir(directory)
	if err != nil {
		failed = append(failed, directory)
		return renamed, failed
	}

	for _, entry := range entries {
		if entry.IsDir() {
			continue
		}

		oldPath := filepath.Join(directory, entry.Name())
		newName := renameFunc(entry.Name())
		newPath := filepath.Join(directory, newName)

		if err := os.Rename(oldPath, newPath); err != nil {
			failed = append(failed, oldPath)
		} else {
			renamed = append(renamed, oldPath+" -> "+newPath)
		}
	}

	return renamed, failed
}

// 5. Safe File Operations

// SafeCopy copies file with safety checks
func SafeCopy(sourcePath, destinationPath string) error {
	// Validate source
	if _, err := os.Stat(sourcePath); err != nil {
		return fmt.Errorf("source file error: %v", err)
	}

	// Check if destination exists
	if _, err := os.Stat(destinationPath); err == nil {
		return fmt.Errorf("destination file already exists")
	}

	return CopyFile(sourcePath, destinationPath)
}

// SafeMove moves file with safety checks
func SafeMove(sourcePath, destinationPath string) error {
	// Validate source
	if _, err := os.Stat(sourcePath); err != nil {
		return fmt.Errorf("source file error: %v", err)
	}

	// Check if destination exists
	if _, err := os.Stat(destinationPath); err == nil {
		return fmt.Errorf("destination file already exists")
	}

	// Ensure destination directory exists
	destDir := filepath.Dir(destinationPath)
	if err := os.MkdirAll(destDir, 0755); err != nil {
		return fmt.Errorf("error creating destination directory: %v", err)
	}

	return MoveFile(sourcePath, destinationPath)
}

// 6. Backup and Restore

// CreateBackup creates backup of files/directories
func CreateBackup(sourcePaths []string, backupDir string, useTimestamp bool) (string, error) {
	var backupPath string

	if useTimestamp {
		timestamp := time.Now().Format("20060102_150405")
		backupPath = filepath.Join(backupDir, "backup_"+timestamp)
	} else {
		backupPath = filepath.Join(backupDir, "backup")
	}

	if err := os.MkdirAll(backupPath, 0755); err != nil {
		return "", fmt.Errorf("error creating backup directory: %v", err)
	}

	for _, source := range sourcePaths {
		dest := filepath.Join(backupPath, filepath.Base(source))

		info, err := os.Stat(source)
		if err != nil {
			continue
		}

		if info.IsDir() {
			if err := CopyDirectory(source, dest); err != nil {
				return "", err
			}
		} else {
			if err := CopyFile(source, dest); err != nil {
				return "", err
			}
		}
	}

	return backupPath, nil
}

// RestoreFromBackup restores files from backup directory
func RestoreFromBackup(backupDir, targetDir string) error {
	return CopyDirectory(backupDir, targetDir)
}

// Usage Examples
func main() {
	fmt.Println("=== Web Go File Copy/Move Examples ===\n")

	// 1. Copy file
	fmt.Println("--- 1. Copy File ---")
	err := CopyFile("source.txt", "copy.txt")
	if err == nil {
		fmt.Println("File copied successfully")
	} else {
		fmt.Printf("Error: %v\n", err)
	}

	// 2. Copy with progress
	fmt.Println("\n--- 2. Copy with Progress ---")
	err = CopyFileWithProgress("source.txt", "copy2.txt", func(written, total int64) {
		fmt.Printf("Progress: %d/%d bytes (%.1f%%)\n", written, total, float64(written)/float64(total)*100)
	})
	if err == nil {
		fmt.Println("File copied with progress successfully")
	}

	// 3. Move file
	fmt.Println("\n--- 3. Move File ---")
	err = MoveFile("old_name.txt", "new_name.txt")
	if err == nil {
		fmt.Println("File moved successfully")
	}

	// 4. Batch rename
	fmt.Println("\n--- 4. Batch Rename ---")
	renamed, failed := BatchRenameFiles(".", func(name string) string {
		return strings.Replace(name, ".txt", ".bak", 1)
	})
	fmt.Printf("Renamed %d files\n", len(renamed))
	fmt.Printf("Failed %d files\n", len(failed))

	// 5. Backup
	fmt.Println("\n--- 5. Backup ---")
	backupPath, err := CreateBackup([]string{"important_file.txt", "important_dir"}, "backups", true)
	if err == nil {
		fmt.Printf("Backup created at: %s\n", backupPath)
	}

	fmt.Println("\n=== All File Copy/Move Examples Completed ===")
}

💻 Parcours de Répertoire go

🔴 complex ⭐⭐⭐⭐

Parcourir les répertoires en utilisant filepath.Walk, les motifs glob et l'exploration de fichiers

⏱️ 30 min 🏷️ go, web, file operations, directory
Prerequisites: Advanced Go, filepath package, ioutil package
// Web Go Directory Traversal Examples
// Using filepath.Walk and glob patterns for directory operations

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

// 1. Directory Listing

// DirectoryContents represents contents of a directory
type DirectoryContents struct {
	Directory   string
	Files       []string
	Directories []string
}

// ListDirectoryContents lists contents of a directory
func ListDirectoryContents(directory string, recursive bool) (*DirectoryContents, error) {
	result := &DirectoryContents{
		Directory:   directory,
		Files:       []string{},
		Directories: []string{},
	}

	if recursive {
		err := filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}

			if info.IsDir() && path != directory {
				result.Directories = append(result.Directories, path)
			} else if !info.IsDir() {
				result.Files = append(result.Files, path)
			}

			return nil
		})

		if err != nil {
			return nil, fmt.Errorf("error walking directory: %v", err)
		}
	} else {
		entries, err := ioutil.ReadDir(directory)
		if err != nil {
			return nil, fmt.Errorf("error reading directory: %v", err)
		}

		for _, entry := range entries {
			fullPath := filepath.Join(directory, entry.Name())

			if entry.IsDir() {
				result.Directories = append(result.Directories, fullPath)
			} else {
				result.Files = append(result.Files, fullPath)
			}
		}
	}

	return result, nil
}

// TreeNode represents a node in the directory tree
type TreeNode struct {
	Name     string
	Path     string
	IsDir    bool
	Children []*TreeNode
}

// ListDirectoryTree lists directory as a tree structure
func ListDirectoryTree(directory string, maxDepth int) (*TreeNode, error) {
	return buildTree(directory, 0, maxDepth)
}

func buildTree(path string, depth, maxDepth int) (*TreeNode, error) {
	if maxDepth >= 0 && depth > maxDepth {
		return &TreeNode{
			Name:     filepath.Base(path),
			Children: []*TreeNode{},
		}, nil
	}

	info, err := os.Stat(path)
	if err != nil {
		return nil, err
	}

	node := &TreeNode{
		Name:  filepath.Base(path),
		Path:  path,
		IsDir: info.IsDir(),
	}

	if info.IsDir() {
		entries, err := ioutil.ReadDir(path)
		if err != nil {
			return node, nil
		}

		// Sort entries
		sort.Slice(entries, func(i, j int) bool {
			return entries[i].Name() < entries[j].Name()
		})

		for _, entry := range entries {
			childPath := filepath.Join(path, entry.Name())
			child, err := buildTree(childPath, depth+1, maxDepth)
			if err != nil {
				continue
			}
			node.Children = append(node.Children, child)
		}
	}

	return node, nil
}

// PrintDirectoryTree prints directory tree structure
func PrintDirectoryTree(tree *TreeNode, indent int) {
	prefix := strings.Repeat("  ", indent)
	icon := "📁"
	if !tree.IsDir {
		icon = "📄"
	}
	fmt.Printf("%s%s %s\n", prefix, icon, tree.Name)

	for _, child := range tree.Children {
		PrintDirectoryTree(child, indent+1)
	}
}

// 2. File Pattern Matching

// FindFilesByPattern finds files matching a pattern
func FindFilesByPattern(directory, pattern string) ([]string, error) {
	var matches []string

	err := filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return nil
		}

		if info.IsDir() {
			return nil
		}

		matched, err := filepath.Match(pattern, filepath.Base(path))
		if err != nil {
			return nil
		}

		if matched {
			matches = append(matches, path)
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	return matches, nil
}

// FindFilesByExtension finds files with specific extension
func FindFilesByExtension(directory, extension string) ([]string, error) {
	if !strings.HasPrefix(extension, ".") {
		extension = "." + extension
	}
	return FindFilesByPattern(directory, "*"+extension)
}

// FindFilesByName finds files by name pattern
func FindFilesByName(directory, namePattern string) ([]string, error) {
	return FindFilesByPattern(directory, namePattern)
}

// 3. Directory Statistics

// DirectoryStats represents statistics about a directory
type DirectoryStats struct {
	TotalFiles       int64
	TotalDirectories int64
	TotalSize        int64
	ExtensionCounts  map[string]int
	LargestFiles     []FileEntry
}

// FileEntry represents a file entry with size
type FileEntry struct {
	Path string
	Size int64
}

// GetDirectoryStatistics gets statistics about a directory
func GetDirectoryStatistics(directory string) (*DirectoryStats, error) {
	stats := &DirectoryStats{
		ExtensionCounts: make(map[string]int),
		LargestFiles:    []FileEntry{},
	}

	err := filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			if path != directory {
				stats.TotalDirectories++
			}
			return nil
		}

		stats.TotalFiles++
		stats.TotalSize += info.Size()

		// Track extensions
		ext := strings.ToLower(filepath.Ext(path))
		stats.ExtensionCounts[ext]++

		// Track largest files
		stats.LargestFiles = append(stats.LargestFiles, FileEntry{
			Path: path,
			Size: info.Size(),
		})

		return nil
	})

	if err != nil {
		return nil, err
	}

	// Sort largest files
	sort.Slice(stats.LargestFiles, func(i, j int) bool {
		return stats.LargestFiles[i].Size > stats.LargestFiles[j].Size
	})

	// Keep top 10
	if len(stats.LargestFiles) > 10 {
		stats.LargestFiles = stats.LargestFiles[:10]
	}

	return stats, nil
}

// 4. File Search in Directory

// SearchInDirectory searches for text in files within a directory
func SearchInDirectory(directory, searchText, filePattern string) (map[string][]int, error) {
	results := make(map[string][]int)

	matches, err := FindFilesByPattern(directory, filePattern)
	if err != nil {
		return nil, err
	}

	for _, filePath := range matches {
		matchingLines, err := searchInFile(filePath, searchText)
		if err != nil {
			continue
		}

		if len(matchingLines) > 0 {
			results[filePath] = matchingLines
		}
	}

	return results, nil
}

func searchInFile(filePath, searchText string) ([]int, error) {
	content, err := ioutil.ReadFile(filePath)
	if err != nil {
		return nil, err
	}

	lines := strings.Split(string(content), "\n")
	var matchingLines []int

	for i, line := range lines {
		if strings.Contains(line, searchText) {
			matchingLines = append(matchingLines, i+1)
		}
	}

	return matchingLines, nil
}

// 5. Directory Comparison

// DirectoryComparison represents comparison results
type DirectoryComparison struct {
	OnlyInDir1 []string
	OnlyInDir2 []string
	Different  []string
	Same       []string
}

// CompareDirectories compares two directories and finds differences
func CompareDirectories(dir1, dir2 string) (*DirectoryComparison, error) {
	result := &DirectoryComparison{
		OnlyInDir1: []string{},
		OnlyInDir2: []string{},
		Different:  []string{},
		Same:       []string{},
	}

	files1 := make(map[string]int64)
	files2 := make(map[string]int64)

	// Walk dir1
	err := filepath.Walk(dir1, func(path string, info os.FileInfo, err error) error {
		if err != nil || info.IsDir() {
			return nil
		}

		relPath, err := filepath.Rel(dir1, path)
		if err != nil {
			return nil
		}

		files1[relPath] = info.Size()
		return nil
	})

	if err != nil {
		return nil, err
	}

	// Walk dir2
	err = filepath.Walk(dir2, func(path string, info os.FileInfo, err error) error {
		if err != nil || info.IsDir() {
			return nil
		}

		relPath, err := filepath.Rel(dir2, path)
		if err != nil {
			return nil
		}

		files2[relPath] = info.Size()
		return nil
	})

	if err != nil {
		return nil, err
	}

	// Find files only in dir1
	for path := range files1 {
		if _, exists := files2[path]; !exists {
			result.OnlyInDir1 = append(result.OnlyInDir1, path)
		}
	}

	// Find files only in dir2
	for path := range files2 {
		if _, exists := files1[path]; !exists {
			result.OnlyInDir2 = append(result.OnlyInDir2, path)
		}
	}

	// Compare common files
	for path := range files1 {
		if size2, exists := files2[path]; exists {
			if files1[path] == size2 {
				result.Same = append(result.Same, path)
			} else {
				result.Different = append(result.Different, path)
			}
		}
	}

	return result, nil
}

// 6. Directory Cleanup

// CleanupDirectory cleans up directory by removing files matching patterns
func CleanupDirectory(directory string, dryRun bool, patterns []string) ([]string, []string) {
	var deleted []string
	var failed []string

	if patterns == nil {
		patterns = []string{"*.tmp", "*.log", "*.bak", "*~"}
	}

	entries, err := ioutil.ReadDir(directory)
	if err != nil {
		failed = append(failed, directory)
		return deleted, failed
	}

	for _, entry := range entries {
		if entry.IsDir() {
			continue
		}

		fileName := entry.Name()
		fullPath := filepath.Join(directory, fileName)

		// Check if matches any pattern
		matches := false
		for _, pattern := range patterns {
			matched, _ := filepath.Match(pattern, fileName)
			if matched {
				matches = true
				break
			}
		}

		if matches {
			if dryRun {
				fmt.Printf("Would delete: %s\n", fullPath)
				deleted = append(deleted, fullPath)
			} else {
				if err := os.Remove(fullPath); err != nil {
					failed = append(failed, fullPath)
				} else {
					deleted = append(deleted, fullPath)
				}
			}
		}
	}

	return deleted, failed
}

// RemoveEmptyDirectories removes empty directories
func RemoveEmptyDirectories(directory string, recursive bool) int {
	removedCount := 0

	if recursive {
		// Process subdirectories first (bottom-up)
		filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
			if err != nil || !info.IsDir() || path == directory {
				return nil
			}

			if isEmptyDir(path) {
				if err := os.Remove(path); err == nil {
					removedCount++
					fmt.Printf("Removed empty directory: %s\n", path)
				}
			}

			return nil
		})
	}

	// Remove the root directory if it's empty
	if isEmptyDir(directory) {
		if err := os.Remove(directory); err == nil {
			removedCount++
			fmt.Printf("Removed empty directory: %s\n", directory)
		}
	}

	return removedCount
}

func isEmptyDir(dir string) bool {
	entries, err := ioutil.ReadDir(dir)
	if err != nil {
		return false
	}
	return len(entries) == 0
}

// Usage Examples
func main() {
	fmt.Println("=== Web Go Directory Traversal Examples ===\n")

	// 1. List directory contents
	fmt.Println("--- 1. List Directory Contents ---")
	contents, err := ListDirectoryContents(".", false)
	if err == nil {
		fmt.Printf("Files: %d\n", len(contents.Files))
		fmt.Printf("Directories: %d\n", len(contents.Directories))
	}

	// 2. Directory tree
	fmt.Println("\n--- 2. Directory Tree ---")
	tree, err := ListDirectoryTree(".", 2)
	if err == nil {
		PrintDirectoryTree(tree, 0)
	}

	// 3. Find files by pattern
	fmt.Println("\n--- 3. Find Files by Pattern ---")
	txtFiles, err := FindFilesByExtension(".", "txt")
	if err == nil {
		fmt.Printf("Text files: %d\n", len(txtFiles))
	}

	// 4. Directory statistics
	fmt.Println("\n--- 4. Directory Statistics ---")
	stats, err := GetDirectoryStatistics(".")
	if err == nil {
		fmt.Printf("Total files: %d\n", stats.TotalFiles)
		fmt.Printf("Total size: %s\n", FormatFileSize(stats.TotalSize))
		fmt.Printf("Extensions: %v\n", stats.ExtensionCounts)
	}

	// 5. Search in directory
	fmt.Println("\n--- 5. Search in Directory ---")
	searchResults, err := SearchInDirectory(".", "TODO", "*.txt")
	if err == nil {
		fmt.Printf("Files containing 'TODO': %d\n", len(searchResults))
	}

	// 6. Compare directories
	fmt.Println("\n--- 6. Compare Directories ---")
	comparison, err := CompareDirectories("dir1", "dir2")
	if err == nil {
		fmt.Printf("Only in dir1: %d\n", len(comparison.OnlyInDir1))
		fmt.Printf("Only in dir2: %d\n", len(comparison.OnlyInDir2))
		fmt.Printf("Different: %d\n", len(comparison.Different))
	}

	// 7. Cleanup
	fmt.Println("\n--- 7. Cleanup ---")
	deleted, _ := CleanupDirectory(".", true, []string{"*.tmp"})
	fmt.Printf("Would delete %d files\n", len(deleted))

	fmt.Println("\n=== All Directory Traversal Examples Completed ===")
}

// FormatFileSize formats file size in human-readable format
func FormatFileSize(bytes int64) string {
	const (
		KB = 1024
		MB = KB * 1024
		GB = MB * 1024
	)

	switch {
	case bytes >= GB:
		return fmt.Sprintf("%.2f GB", float64(bytes)/float64(GB))
	case bytes >= MB:
		return fmt.Sprintf("%.2f MB", float64(bytes)/float64(MB))
	case bytes >= KB:
		return fmt.Sprintf("%.2f KB", float64(bytes)/float64(KB))
	default:
		return fmt.Sprintf("%d B", bytes)
	}
}