Working with files and directories is a fundamental aspect of many applications. Whether you're developing a simple console app or a complex enterprise solution, understanding how to manipulate the file system is essential. In this tutorial, we'll delve deep into how to work with files and directories in C#, complete with examples, use cases, and a real-world application.
The ability to interact with the file system allows applications to store data persistently, read configuration files, log events, and much more. C# provides robust classes within the System.IO namespace to handle file and directory operations seamlessly.
System.IO NamespaceThe System.IO namespace contains types that allow reading and writing to files and data streams, and types that provide basic file and directory support.
Key classes include:
File: Provides static methods for creating, copying, deleting, moving, and opening files.FileInfo: Provides instance methods for the same operations as the File class, but the methods are instance-based.Directory: Provides static methods for creating, moving, and enumerating through directories and subdirectories.DirectoryInfo: Provides instance methods for the same operations as the Directory class.StreamReader and StreamWriter: For reading from and writing to streams.FileStream: Provides a stream for a file, supporting both synchronous and asynchronous read and write operations.To create a file, you can use the File.Create method or the FileStream class.
File.Create:using System.IO;
class Program
{
static void Main()
{
string path = @"C:\temp\myfile.txt";
File.Create(path).Dispose(); // Dispose to release the handle
}
}
File.Create method creates a file at the specified path.Dispose() or use a using statement to release the file handle immediately.You can write to a file using the StreamWriter class or the File.WriteAllText method.
StreamWriter:using System.IO;
class Program
{
static void Main()
{
string path = @"C:\temp\myfile.txt";
using (StreamWriter writer = new StreamWriter(path))
{
writer.WriteLine("Hello, World!");
}
}
}
StreamWriter writes characters to a stream in a particular encoding.using statement ensures the StreamWriter is disposed of properly.To read from a file, use the StreamReader class or File.ReadAllText.
StreamReader:using System;
using System.IO;
class Program
{
static void Main()
{
string path = @"C:\temp\myfile.txt";
using (StreamReader reader = new StreamReader(path))
{
string content = reader.ReadToEnd();
Console.WriteLine(content);
}
}
}
StreamReader reads characters from a byte stream in a particular encoding.ReadToEnd reads the stream from the current position to the end.To append text to an existing file, you can use StreamWriter with true as the second parameter or File.AppendAllText.
StreamWriter to append:using System.IO;
class Program
{
static void Main()
{
string path = @"C:\temp\myfile.txt";
using (StreamWriter writer = new StreamWriter(path, true))
{
writer.WriteLine("Appending a new line.");
}
}
}
Explanation:
true to StreamWriter opens the file in append mode.To delete a file, use the File.Delete method.
using System.IO;
class Program
{
static void Main()
{
string path = @"C:\temp\myfile.txt";
if (File.Exists(path))
{
File.Delete(path);
Console.WriteLine("File deleted.");
}
else
{
Console.WriteLine("File does not exist.");
}
}
}
Use the Directory.CreateDirectory method to create a directory.
using System.IO;
class Program
{
static void Main()
{
string path = @"C:\temp\myfolder";
Directory.CreateDirectory(path);
}
}
To list directories and files, use methods like Directory.GetFiles and Directory.GetDirectories.
using System;
using System.IO;
class Program
{
static void Main()
{
string path = @"C:\temp";
// List all files
string[] files = Directory.GetFiles(path);
Console.WriteLine("Files:");
foreach (string file in files)
{
Console.WriteLine(file);
}
// List all directories
string[] directories = Directory.GetDirectories(path);
Console.WriteLine("\nDirectories:");
foreach (string directory in directories)
{
Console.WriteLine(directory);
}
}
}
GetFiles retrieves the names of files in a specified directory.GetDirectories retrieves the names of subdirectories.using System.IO;
class Program
{
static void Main()
{
string sourceDir = @"C:\temp\myfolder";
string destDir = @"C:\temp\mynewfolder";
if (Directory.Exists(sourceDir))
{
Directory.Move(sourceDir, destDir);
Console.WriteLine("Directory moved.");
}
else
{
Console.WriteLine("Source directory does not exist.");
}
}
}
using System.IO;
class Program
{
static void Main()
{
string path = @"C:\temp\myfolder";
if (Directory.Exists(path))
{
Directory.Delete(path, true); // 'true' to delete subdirectories and files
Console.WriteLine("Directory deleted.");
}
else
{
Console.WriteLine("Directory does not exist.");
}
}
}
Directory.Move moves a directory to a new location.Directory.Delete deletes a directory. The second parameter indicates whether to delete subdirectories and files.The FileInfo and DirectoryInfo classes provide instance methods for file and directory operations and offer additional properties.
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = @"C:\temp\myfile.txt";
FileInfo fileInfo = new FileInfo(filePath);
if (fileInfo.Exists)
{
Console.WriteLine("File Size: " + fileInfo.Length);
Console.WriteLine("Created On: " + fileInfo.CreationTime);
Console.WriteLine("Last Accessed: " + fileInfo.LastAccessTime);
}
else
{
Console.WriteLine("File does not exist.");
}
}
}
Explanation:
FileInfo provides properties like Length, CreationTime, and LastAccessTime.Let's build a simple application that logs messages to daily log files and archives old logs.
using System;
using System.IO;
class LogManager
{
private string logDirectory;
private string archiveDirectory;
public LogManager(string logDir)
{
logDirectory = logDir;
archiveDirectory = Path.Combine(logDirectory, "Archive");
// Ensure directories exist
Directory.CreateDirectory(logDirectory);
Directory.CreateDirectory(archiveDirectory);
}
public void WriteLog(string message)
{
string logFile = Path.Combine(logDirectory, DateTime.Now.ToString("yyyy-MM-dd") + ".log");
using (StreamWriter writer = new StreamWriter(logFile, true))
{
writer.WriteLine($"{DateTime.Now:HH:mm:ss} - {message}");
}
}
public void ArchiveOldLogs(int days)
{
string[] logFiles = Directory.GetFiles(logDirectory, "*.log");
foreach (string file in logFiles)
{
FileInfo fileInfo = new FileInfo(file);
if (fileInfo.CreationTime < DateTime.Now.AddDays(-days))
{
string archivedPath = Path.Combine(archiveDirectory, fileInfo.Name);
if (File.Exists(archivedPath))
{
File.Delete(archivedPath);
}
File.Move(file, archivedPath);
Console.WriteLine($"Archived: {fileInfo.Name}");
}
}
}
}
class Program
{
static void Main()
{
LogManager logManager = new LogManager(@"C:\temp\logs");
// Write some logs
logManager.WriteLog("Application started.");
logManager.WriteLog("Performing some operations.");
logManager.WriteLog("Application ended.");
// Archive logs older than 7 days
logManager.ArchiveOldLogs(7);
}
}
LogManager Class:WriteLog writes messages to a daily log file.ArchiveOldLogs moves logs older than a specified number of days to an archive directory.LogManager with the desired log directory.WriteLog to log messages.ArchiveOldLogs to archive old logs.Use Cases:
System.IO Namespace: Essential for file and directory operations in C#.File vs. FileInfo: File provides static methods; FileInfo provides instance methods with additional properties.Directory vs. DirectoryInfo: Similar to File and FileInfo, but for directories.StreamReader and StreamWriter: For reading from and writing to text files.Using Statements: Ensure resources like file handles are released promptly.Path class to handle file and directory paths efficiently and avoid hardcoding separators.Working with files and directories is a crucial skill for any C# developer. The System.IO namespace provides all the necessary classes and methods to create, read, write, move, and delete files and directories. Understanding how to leverage these tools allows you to manage data storage, configuration, logging, and more effectively. By applying these concepts, as demonstrated in the log file manager example, you can build robust applications that interact seamlessly with the file system.