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.