Skip to content

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

csharp
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

csharp
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

csharp
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

csharp
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

csharp
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

csharp
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

csharp
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

csharp
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

csharp
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

csharp
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

csharp
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

csharp
// 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

csharp
// 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.

Content is for learning and research only.