C# File I/O

Overview

File I/O operations allow you to read from and write to files on the file system. C# provides comprehensive classes for file manipulation in the System.IO namespace.

Basic File Operations

Reading Files

using System.IO;

public class FileReader
{
    // Read all text at once
    public string ReadAllText(string filePath)
    {
        try
        {
            return File.ReadAllText(filePath);
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine($"File not found: {filePath}");
            return string.Empty;
        }
        catch (IOException ex)
        {
            Console.WriteLine($"IO error: {ex.Message}");
            return string.Empty;
        }
    }
    
    // Read all lines
    public string[] ReadAllLines(string filePath)
    {
        try
        {
            return File.ReadAllLines(filePath);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error reading file: {ex.Message}");
            return new string[0];
        }
    }
    
    // Read using StreamReader
    public async Task<string> ReadFileAsync(string filePath)
    {
        try
        {
            using StreamReader reader = new StreamReader(filePath);
            return await reader.ReadToEndAsync();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error reading file: {ex.Message}");
            return string.Empty;
        }
    }
    
    // Read line by line
    public async Task<List<string>> ReadLinesAsync(string filePath)
    {
        var lines = new List<string>();
        
        try
        {
            using StreamReader reader = new StreamReader(filePath)
            {
                string line;
                while ((line = await reader.ReadLineAsync()) != null)
                {
                    lines.Add(line);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error reading file: {ex.Message}");
        }
        
        return lines;
    }
}

Writing Files

public class FileWriter
{
    // Write all text at once
    public bool WriteAllText(string filePath, string content)
    {
        try
        {
            File.WriteAllText(filePath, content);
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error writing file: {ex.Message}");
            return false;
        }
    }
    
    // Write all lines
    public bool WriteAllLines(string filePath, string[] lines)
    {
        try
        {
            File.WriteAllLines(filePath, lines);
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error writing file: {ex.Message}");
            return false;
        }
    }
    
    // Append to file
    public bool AppendToFile(string filePath, string content)
    {
        try
        {
            File.AppendAllText(filePath, content);
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error appending to file: {ex.Message}");
            return false;
        }
    }
    
    // Write using StreamWriter
    public async Task<bool> WriteFileAsync(string filePath, string content)
    {
        try
        {
            using StreamWriter writer = new StreamWriter(filePath);
            await writer.WriteAsync(content);
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error writing file: {ex.Message}");
            return false;
        }
    }
    
    // Write line by line
    public async Task<bool> WriteLinesAsync(string filePath, List<string> lines)
    {
        try
        {
            using StreamWriter writer = new StreamWriter(filePath)
            {
                foreach (string line in lines)
                {
                    await writer.WriteLineAsync(line);
                }
            }
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error writing file: {ex.Message}");
            return false;
        }
    }
}

File and Directory Operations

File Operations

public class FileOperations
{
    // Check if file exists
    public bool FileExists(string filePath)
    {
        return File.Exists(filePath);
    }
    
    // Copy file
    public bool CopyFile(string sourcePath, string destinationPath)
    {
        try
        {
            File.Copy(sourcePath, destinationPath, true); // overwrite if exists
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error copying file: {ex.Message}");
            return false;
        }
    }
    
    // Move file
    public bool MoveFile(string sourcePath, string destinationPath)
    {
        try
        {
            File.Move(sourcePath, destinationPath);
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error moving file: {ex.Message}");
            return false;
        }
    }
    
    // Delete file
    public bool DeleteFile(string filePath)
    {
        try
        {
            if (File.Exists(filePath))
            {
                File.Delete(filePath);
                return true;
            }
            return false;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error deleting file: {ex.Message}");
            return false;
        }
    }
    
    // Get file information
    public FileInfo GetFileInfo(string filePath)
    {
        try
        {
            return new FileInfo(filePath);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error getting file info: {ex.Message}");
            return null;
        }
    }
}

Directory Operations

public class DirectoryOperations
{
    // Check if directory exists
    public bool DirectoryExists(string directoryPath)
    {
        return Directory.Exists(directoryPath);
    }
    
    // Create directory
    public bool CreateDirectory(string directoryPath)
    {
        try
        {
            Directory.CreateDirectory(directoryPath);
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error creating directory: {ex.Message}");
            return false;
        }
    }
    
    // Delete directory
    public bool DeleteDirectory(string directoryPath, bool recursive = false)
    {
        try
        {
            if (Directory.Exists(directoryPath))
            {
                Directory.Delete(directoryPath, recursive);
                return true;
            }
            return false;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error deleting directory: {ex.Message}");
            return false;
        }
    }
    
    // Get files in directory
    public string[] GetFiles(string directoryPath, string searchPattern = "*")
    {
        try
        {
            return Directory.GetFiles(directoryPath, searchPattern);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error getting files: {ex.Message}");
            return new string[0];
        }
    }
    
    // Get subdirectories
    public string[] GetDirectories(string directoryPath)
    {
        try
        {
            return Directory.GetDirectories(directoryPath);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error getting directories: {ex.Message}");
            return new string[0];
        }
    }
    
    // Move directory
    public bool MoveDirectory(string sourcePath, string destinationPath)
    {
        try
        {
            Directory.Move(sourcePath, destinationPath);
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error moving directory: {ex.Message}");
            return false;
        }
    }
}

Working with Paths

Path Operations

public class PathOperations
{
    // Combine paths
    public string CombinePaths(params string[] paths)
    {
        return Path.Combine(paths);
    }
    
    // Get directory name
    public string GetDirectoryName(string filePath)
    {
        return Path.GetDirectoryName(filePath);
    }
    
    // Get file name
    public string GetFileName(string filePath)
    {
        return Path.GetFileName(filePath);
    }
    
    // Get file name without extension
    public string GetFileNameWithoutExtension(string filePath)
    {
        return Path.GetFileNameWithoutExtension(filePath);
    }
    
    // Get extension
    public string GetExtension(string filePath)
    {
        return Path.GetExtension(filePath);
    }
    
    // Change extension
    public string ChangeExtension(string filePath, string newExtension)
    {
        return Path.ChangeExtension(filePath, newExtension);
    }
    
    // Get absolute path
    public string GetAbsolutePath(string relativePath)
    {
        return Path.GetFullPath(relativePath);
    }
    
    // Get temporary file path
    public string GetTempFilePath()
    {
        return Path.GetTempFileName();
    }
    
    // Get random file name
    public string GetRandomFileName()
    {
        return Path.GetRandomFileName();
    }
}

Advanced File Operations

File Monitoring

using System.IO;

public class FileMonitor
{
    private FileSystemWatcher _watcher;
    
    public event EventHandler<FileEventArgs> FileCreated;
    public event EventHandler<FileEventArgs> FileDeleted;
    public event EventHandler<FileEventArgs> FileChanged;
    
    public void StartMonitoring(string directoryPath, string filter = "*.*")
    {
        _watcher = new FileSystemWatcher(directoryPath, filter)
        {
            NotifyFilter = NotifyFilters.FileName | 
                         NotifyFilters.LastWrite | 
                         NotifyFilters.Size
        };
        
        _watcher.Created += OnFileCreated;
        _watcher.Deleted += OnFileDeleted;
        _watcher.Changed += OnFileChanged;
        
        _watcher.EnableRaisingEvents = true;
        Console.WriteLine($"Started monitoring: {directoryPath}");
    }
    
    public void StopMonitoring()
    {
        if (_watcher != null)
        {
            _watcher.EnableRaisingEvents = false;
            _watcher.Dispose();
            Console.WriteLine("Stopped monitoring");
        }
    }
    
    private void OnFileCreated(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine($"File created: {e.FullPath}");
        FileCreated?.Invoke(this, new FileEventArgs(e.FullPath, "Created"));
    }
    
    private void OnFileDeleted(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine($"File deleted: {e.FullPath}");
        FileDeleted?.Invoke(this, new FileEventArgs(e.FullPath, "Deleted"));
    }
    
    private void OnFileChanged(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine($"File changed: {e.FullPath}");
        FileChanged?.Invoke(this, new FileEventArgs(e.FullPath, "Changed"));
    }
}

public class FileEventArgs : EventArgs
{
    public string FilePath { get; }
    public string Action { get; }
    
    public FileEventArgs(string filePath, string action)
    {
        FilePath = filePath;
        Action = action;
    }
}

File Compression

using System.IO.Compression;

public class FileCompression
{
    // Compress file
    public async Task<bool> CompressFileAsync(string sourcePath, string destinationPath)
    {
        try
        {
            using FileStream sourceStream = new FileStream(sourcePath, FileMode.Open);
            using FileStream destinationStream = new FileStream(destinationPath, FileMode.Create);
            using GZipStream compressionStream = new GZipStream(destinationStream, CompressionMode.Compress)
            {
                await sourceStream.CopyToAsync(compressionStream);
            }
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error compressing file: {ex.Message}");
            return false;
        }
    }
    
    // Decompress file
    public async Task<bool> DecompressFileAsync(string sourcePath, string destinationPath)
    {
        try
        {
            using FileStream sourceStream = new FileStream(sourcePath, FileMode.Open);
            using FileStream destinationStream = new FileStream(destinationPath, FileMode.Create);
            using GZipStream decompressionStream = new GZipStream(sourceStream, CompressionMode.Decompress)
            {
                await decompressionStream.CopyToAsync(destinationStream);
            }
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error decompressing file: {ex.Message}");
            return false;
        }
    }
    
    // Compress directory
    public async Task<bool> CompressDirectoryAsync(string sourceDirectory, string destinationZip)
    {
        try
        {
            await Task.Run(() => ZipFile.CreateFromDirectory(sourceDirectory, destinationZip));
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error compressing directory: {ex.Message}");
            return false;
        }
    }
    
    // Extract directory
    public async Task<bool> ExtractDirectoryAsync(string sourceZip, string destinationDirectory)
    {
        try
        {
            await Task.Run(() => ZipFile.ExtractToDirectory(sourceZip, destinationDirectory));
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error extracting directory: {ex.Message}");
            return false;
        }
    }
}

Binary File Operations

Reading and Writing Binary Files

public class BinaryFileOperations
{
    // Write binary data
    public bool WriteBinaryFile(string filePath, byte[] data)
    {
        try
        {
            File.WriteAllBytes(filePath, data);
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error writing binary file: {ex.Message}");
            return false;
        }
    }
    
    // Read binary data
    public byte[] ReadBinaryFile(string filePath)
    {
        try
        {
            return File.ReadAllBytes(filePath);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error reading binary file: {ex.Message}");
            return new byte[0];
        }
    }
    
    // Write objects to binary file
    public bool WriteObjectToFile<T>(string filePath, T obj)
    {
        try
        {
            using FileStream stream = new FileStream(filePath, FileMode.Create);
            using BinaryWriter writer = new BinaryWriter(stream)
            {
                // Simple serialization (for complex objects, use JsonSerializer or BinaryFormatter)
                string json = System.Text.Json.JsonSerializer.Serialize(obj);
                writer.Write(json);
            }
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error writing object to file: {ex.Message}");
            return false;
        }
    }
    
    // Read objects from binary file
    public T ReadObjectFromFile<T>(string filePath)
    {
        try
        {
            using FileStream stream = new FileStream(filePath, FileMode.Open);
            using BinaryReader reader = new BinaryReader(stream)
            {
                string json = reader.ReadString();
                return System.Text.Json.JsonSerializer.Deserialize<T>(json);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error reading object from file: {ex.Message}");
            return default(T);
        }
    }
}

Practical Examples

Log File Manager

public class LogManager
{
    private readonly string _logDirectory;
    private readonly string _logFilePath;
    private readonly object _lockObject = new object();
    
    public LogManager(string logDirectory = "logs")
    {
        _logDirectory = logDirectory;
        
        if (!Directory.Exists(_logDirectory))
        {
            Directory.CreateDirectory(_logDirectory);
        }
        
        _logFilePath = Path.Combine(_logDirectory, $"app_{DateTime.Now:yyyyMMdd}.log");
    }
    
    public void LogInfo(string message)
    {
        Log("INFO", message);
    }
    
    public void LogWarning(string message)
    {
        Log("WARNING", message);
    }
    
    public void LogError(string message, Exception exception = null)
    {
        string fullMessage = exception != null 
            ? $"{message}\nException: {exception.Message}\nStack Trace: {exception.StackTrace}"
            : message;
        Log("ERROR", fullMessage);
    }
    
    private void Log(string level, string message)
    {
        lock (_lockObject)
        {
            string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [{level}] {message}";
            
            try
            {
                File.AppendAllText(_logFilePath, logEntry + Environment.NewLine);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Failed to write to log file: {ex.Message}");
            }
        }
    }
    
    public List<string> GetRecentLogs(int lines = 100)
    {
        try
        {
            if (!File.Exists(_logFilePath))
                return new List<string>();
            
            var allLines = File.ReadAllLines(_logFilePath);
            return allLines.TakeLast(lines).ToList();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error reading log file: {ex.Message}");
            return new List<string>();
        }
    }
}

Configuration Manager

public class ConfigurationManager
{
    private readonly string _configFilePath;
    private Dictionary<string, string> _settings;
    private readonly object _lockObject = new object();
    
    public ConfigurationManager(string configFilePath = "appsettings.json")
    {
        _configFilePath = configFilePath;
        _settings = new Dictionary<string, string>();
        LoadSettings();
    }
    
    public string GetSetting(string key, string defaultValue = "")
    {
        lock (_lockObject)
        {
            return _settings.TryGetValue(key, out string value) ? value : defaultValue;
        }
    }
    
    public void SetSetting(string key, string value)
    {
        lock (_lockObject)
        {
            _settings[key] = value;
            SaveSettings();
        }
    }
    
    public T GetSetting<T>(string key, T defaultValue = default(T))
    {
        string value = GetSetting(key);
        
        if (string.IsNullOrEmpty(value))
            return defaultValue;
        
        try
        {
            return (T)Convert.ChangeType(value, typeof(T));
        }
        catch
        {
            return defaultValue;
        }
    }
    
    private void LoadSettings()
    {
        try
        {
            if (File.Exists(_configFilePath))
            {
                string json = File.ReadAllText(_configFilePath);
                _settings = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(json) 
                          ?? new Dictionary<string, string>();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error loading settings: {ex.Message}");
        }
    }
    
    private void SaveSettings()
    {
        try
        {
            string json = System.Text.Json.JsonSerializer.Serialize(_settings, new System.Text.Json.JsonSerializerOptions
            {
                WriteIndented = true
            });
            File.WriteAllText(_configFilePath, json);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error saving settings: {ex.Message}");
        }
    }
}

File Backup Utility

public class FileBackupUtility
{
    public async Task<bool> BackupFilesAsync(string sourceDirectory, string backupDirectory, string[] fileExtensions = null)
    {
        try
        {
            if (!Directory.Exists(sourceDirectory))
            {
                Console.WriteLine($"Source directory does not exist: {sourceDirectory}");
                return false;
            }
            
            if (!Directory.Exists(backupDirectory))
            {
                Directory.CreateDirectory(backupDirectory);
            }
            
            string searchPattern = fileExtensions != null && fileExtensions.Length > 0 
                ? "*.*" 
                : "*.*";
            
            var files = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories);
            
            foreach (string sourceFile in files)
            {
                if (fileExtensions != null && fileExtensions.Length > 0)
                {
                    string extension = Path.GetExtension(sourceFile);
                    if (!fileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
                        continue;
                }
                
                string relativePath = Path.GetRelativePath(sourceDirectory, sourceFile);
                string backupFile = Path.Combine(backupDirectory, relativePath);
                
                string backupDir = Path.GetDirectoryName(backupFile);
                if (!Directory.Exists(backupDir))
                {
                    Directory.CreateDirectory(backupDir);
                }
                
                await CopyFileWithProgressAsync(sourceFile, backupFile);
            }
            
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error during backup: {ex.Message}");
            return false;
        }
    }
    
    private async Task CopyFileWithProgressAsync(string sourceFile, string destinationFile)
    {
        using FileStream sourceStream = new FileStream(sourceFile, FileMode.Open);
        using FileStream destinationStream = new FileStream(destinationFile, FileMode.Create);
        {
            byte[] buffer = new byte[8192];
            int bytesRead;
            
            while ((bytesRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
            {
                await destinationStream.WriteAsync(buffer, 0, bytesRead);
            }
        }
        
        Console.WriteLine($"Backed up: {Path.GetFileName(sourceFile)}");
    }
}

Best Practices

File I/O Guidelines

// Good: Use using statements for proper disposal
public void GoodFileHandling()
{
    using StreamReader reader = new StreamReader("file.txt")
    {
        string content = reader.ReadToEnd();
        // reader is automatically disposed
    }
}

// Bad: Forgetting to dispose
public void BadFileHandling()
{
    StreamReader reader = new StreamReader("file.txt");
    string content = reader.ReadToEnd();
    // reader is not disposed - resource leak
}

// Good: Use async methods for I/O operations
public async Task<string> GoodAsyncFileRead()
{
    using StreamReader reader = new StreamReader("file.txt")
    {
        return await reader.ReadToEndAsync();
    }
}

// Good: Handle exceptions properly
public bool SafeFileOperation()
{
    try
    {
        File.WriteAllText("output.txt", "content");
        return true;
    }
    catch (UnauthorizedAccessException)
    {
        Console.WriteLine("Access denied");
        return false;
    }
    catch (IOException ex)
    {
        Console.WriteLine($"IO error: {ex.Message}");
        return false;
    }
}

Performance Considerations

// Good: Use appropriate buffer size
public async Task EfficientFileCopy(string source, string destination)
{
    const int bufferSize = 8192; // 8KB buffer
    
    using FileStream sourceStream = new FileStream(source, FileMode.Open);
    using FileStream destinationStream = new FileStream(destination, FileMode.Create);
    {
        byte[] buffer = new byte[bufferSize];
        int bytesRead;
        
        while ((bytesRead = await sourceStream.ReadAsync(buffer, 0, bufferSize)) > 0)
        {
            await destinationStream.WriteAsync(buffer, 0, bytesRead);
        }
    }
}

// Good: Use FileOptions for better performance
public async Task OptimizedFileWrite(string filePath, string content)
{
    byte[] bytes = Encoding.UTF8.GetBytes(content);
    
    using FileStream stream = new FileStream(
        filePath, 
        FileMode.Create, 
        FileAccess.Write, 
        FileShare.None, 
        bufferSize: 4096, 
        FileOptions.Asynchronous)
    {
        await stream.WriteAsync(bytes, 0, bytes.Length);
    }
}

Summary

In this chapter, you learned:

  • Basic file reading and writing operations
  • Directory operations and path manipulation
  • Advanced file operations like monitoring and compression
  • Binary file operations
  • Practical examples like logging and configuration management
  • Best practices for file I/O operations
  • Performance considerations and optimization techniques

File I/O is fundamental for many applications that need to persist data or interact with the file system. Mastering these operations will help you build robust data management solutions.