C# File Operations
Introduction
File operations are a fundamental part of many applications. Whether you're building a text editor, a data processing tool, or a system utility, you'll often need to interact with files on the filesystem. C# provides robust and easy-to-use classes for various file operations through the System.IO
namespace.
In this tutorial, we'll explore common file operations including:
- Checking if files exist
- Creating files
- Reading from files
- Writing to files
- Copying and moving files
- Deleting files
- Getting file information
These operations form the building blocks for more complex file handling tasks in your applications.
Prerequisites
Before we dive in, make sure you have:
- Basic knowledge of C# syntax
- A development environment (Visual Studio, VS Code, etc.) with .NET installed
- Understanding of basic programming concepts
Let's start by adding the required namespace to our code:
using System;
using System.IO;
Checking if a File Exists
Before performing operations on a file, it's often necessary to check if the file exists:
string filePath = @"C:\example\sample.txt";
if (File.Exists(filePath))
{
Console.WriteLine($"The file {filePath} exists.");
}
else
{
Console.WriteLine($"The file {filePath} does not exist.");
}
Output (if file doesn't exist):
The file C:\example\sample.txt does not exist.
Creating Files
C# offers multiple ways to create files. Here are two common approaches:
1. Using File.Create()
string filePath = @"C:\example\newfile.txt";
try
{
// Create a file - returns a FileStream
using (FileStream fs = File.Create(filePath))
{
// File created successfully
Console.WriteLine($"File created at {filePath}");
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
2. Using StreamWriter
string filePath = @"C:\example\anotherfile.txt";
try
{
// Create a file and write some initial content
using (StreamWriter writer = new StreamWriter(filePath))
{
writer.WriteLine("This is the first line.");
Console.WriteLine($"File created at {filePath} with initial content");
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
Reading from Files
There are several ways to read from files in C#:
1. Reading All Text at Once
string filePath = @"C:\example\sample.txt";
try
{
// Read all content at once
string content = File.ReadAllText(filePath);
Console.WriteLine("File content:");
Console.WriteLine(content);
}
catch (FileNotFoundException)
{
Console.WriteLine($"The file {filePath} was not found.");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
2. Reading Line by Line
string filePath = @"C:\example\sample.txt";
try
{
// Read all lines into a string array
string[] lines = File.ReadAllLines(filePath);
Console.WriteLine("File content line by line:");
for (int i = 0; i < lines.Length; i++)
{
Console.WriteLine($"Line {i+1}: {lines[i]}");
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
3. Reading with StreamReader
string filePath = @"C:\example\sample.txt";
try
{
using (StreamReader reader = new StreamReader(filePath))
{
string line;
int lineNumber = 1;
Console.WriteLine("File content (using StreamReader):");
// Read line by line until the end of the file
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine($"Line {lineNumber++}: {line}");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
Writing to Files
Similar to reading, C# provides multiple ways to write content to files:
1. Writing All Text at Once
string filePath = @"C:\example\output.txt";
string content = "Hello, world!\nThis is a sample text.\nWritten using C#.";
try
{
// Write all text at once (overwrites existing content)
File.WriteAllText(filePath, content);
Console.WriteLine($"Content written to {filePath}");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
2. Writing Line by Line
string filePath = @"C:\example\lines.txt";
string[] lines = {
"First line",
"Second line",
"Third line",
"Fourth line"
};
try
{
// Write all lines at once
File.WriteAllLines(filePath, lines);
Console.WriteLine($"Lines written to {filePath}");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
3. Appending to Files
string filePath = @"C:\example\log.txt";
string newEntry = $"[{DateTime.Now}] Application started";
try
{
// Append text to existing file
File.AppendAllText(filePath, newEntry + Environment.NewLine);
Console.WriteLine($"Entry appended to {filePath}");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
4. Using StreamWriter
string filePath = @"C:\example\detailed.txt";
try
{
// true parameter enables appending
using (StreamWriter writer = new StreamWriter(filePath, true))
{
writer.WriteLine("This is a line written using StreamWriter");
writer.WriteLine($"Current timestamp: {DateTime.Now}");
writer.WriteLine(new string('-', 30)); // Separator line
}
Console.WriteLine($"Content written to {filePath}");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
Copying and Moving Files
Copying Files
string sourceFile = @"C:\example\source.txt";
string destinationFile = @"C:\example\backup\source-copy.txt";
try
{
// Create directory if it doesn't exist
Directory.CreateDirectory(Path.GetDirectoryName(destinationFile));
// Copy file (set overwrite to true)
File.Copy(sourceFile, destinationFile, true);
Console.WriteLine($"File copied from {sourceFile} to {destinationFile}");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
Moving Files
string sourceFile = @"C:\example\source.txt";
string destinationFile = @"C:\example\moved\source.txt";
try
{
// Create directory if it doesn't exist
Directory.CreateDirectory(Path.GetDirectoryName(destinationFile));
// Move file
File.Move(sourceFile, destinationFile);
Console.WriteLine($"File moved from {sourceFile} to {destinationFile}");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
Deleting Files
string filePath = @"C:\example\temp.txt";
try
{
// Check if file exists before attempting to delete
if (File.Exists(filePath))
{
File.Delete(filePath);
Console.WriteLine($"File {filePath} deleted successfully");
}
else
{
Console.WriteLine($"File {filePath} does not exist");
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
Getting File Information
The FileInfo
class provides useful methods and properties for working with files:
string filePath = @"C:\example\sample.txt";
try
{
FileInfo fileInfo = new FileInfo(filePath);
if (fileInfo.Exists)
{
Console.WriteLine($"File name: {fileInfo.Name}");
Console.WriteLine($"Full path: {fileInfo.FullName}");
Console.WriteLine($"Directory: {fileInfo.DirectoryName}");
Console.WriteLine($"Size: {fileInfo.Length} bytes");
Console.WriteLine($"Extension: {fileInfo.Extension}");
Console.WriteLine($"Creation time: {fileInfo.CreationTime}");
Console.WriteLine($"Last access time: {fileInfo.LastAccessTime}");
Console.WriteLine($"Last write time: {fileInfo.LastWriteTime}");
Console.WriteLine($"Attributes: {fileInfo.Attributes}");
}
else
{
Console.WriteLine($"The file {filePath} does not exist.");
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
Practical Example: Simple Log File System
Let's create a practical example of a simple logging system that:
- Creates a log file if it doesn't exist
- Appends log entries with timestamps
- Can display the most recent logs
public class SimpleLogger
{
private readonly string _logFilePath;
public SimpleLogger(string logFilePath)
{
_logFilePath = logFilePath;
// Create directory if it doesn't exist
string directory = Path.GetDirectoryName(_logFilePath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
}
public void Log(string message, string level = "INFO")
{
string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [{level}] {message}";
try
{
// Append the log entry
File.AppendAllText(_logFilePath, logEntry + Environment.NewLine);
}
catch (Exception ex)
{
Console.WriteLine($"Failed to write to log file: {ex.Message}");
}
}
public void DisplayLastNLogs(int n)
{
try
{
if (!File.Exists(_logFilePath))
{
Console.WriteLine("Log file does not exist yet.");
return;
}
// Read all lines and get the last N entries
string[] allLogs = File.ReadAllLines(_logFilePath);
int startIndex = Math.Max(0, allLogs.Length - n);
Console.WriteLine($"Last {Math.Min(n, allLogs.Length)} log entries:");
for (int i = startIndex; i < allLogs.Length; i++)
{
Console.WriteLine(allLogs[i]);
}
}
catch (Exception ex)
{
Console.WriteLine($"Error reading logs: {ex.Message}");
}
}
}
// Example usage
public void LoggingExample()
{
SimpleLogger logger = new SimpleLogger(@"C:\logs\application.log");
// Log some messages
logger.Log("Application started");
logger.Log("User logged in: JohnDoe", "USER");
logger.Log("Database connection established", "DB");
logger.Log("Invalid password attempt for user: guest", "WARNING");
logger.Log("System exception occurred: Out of memory", "ERROR");
// Display last 3 logs
logger.DisplayLastNLogs(3);
}
Output:
Last 3 log entries:
[2023-09-25 14:32:45] [DB] Database connection established
[2023-09-25 14:32:45] [WARNING] Invalid password attempt for user: guest
[2023-09-25 14:32:45] [ERROR] System exception occurred: Out of memory
Practical Example: File Backup Utility
Here's another real-world example - a simple file backup utility:
public class FileBackup
{
public void BackupFile(string sourcePath, string backupDirectory, bool appendTimestamp = true)
{
try
{
// Ensure source file exists
if (!File.Exists(sourcePath))
{
Console.WriteLine($"Source file not found: {sourcePath}");
return;
}
// Create backup directory if it doesn't exist
if (!Directory.Exists(backupDirectory))
{
Directory.CreateDirectory(backupDirectory);
Console.WriteLine($"Created backup directory: {backupDirectory}");
}
// Get file name from path
string fileName = Path.GetFileName(sourcePath);
string destinationPath;
if (appendTimestamp)
{
// Add timestamp to filename
string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
string extension = Path.GetExtension(fileName);
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
destinationPath = Path.Combine(
backupDirectory,
$"{fileNameWithoutExt}_{timestamp}{extension}"
);
}
else
{
destinationPath = Path.Combine(backupDirectory, fileName);
}
// Copy the file
File.Copy(sourcePath, destinationPath, true);
Console.WriteLine($"Backed up {sourcePath} to {destinationPath}");
// Get file information
FileInfo fileInfo = new FileInfo(destinationPath);
Console.WriteLine($"Backup size: {fileInfo.Length} bytes");
}
catch (Exception ex)
{
Console.WriteLine($"Backup failed: {ex.Message}");
}
}
}
// Example usage
public void BackupExample()
{
FileBackup backup = new FileBackup();
backup.BackupFile(
@"C:\projects\important-document.docx",
@"C:\backups\documents"
);
}
Output:
Backed up C:\projects\important-document.docx to C:\backups\documents\important-document_20230925_143512.docx
Backup size: 24576 bytes
Best Practices for File Operations
When working with files in C#, keep these best practices in mind:
-
Always use exception handling: File operations are prone to errors due to permissions, locks, or network issues.
-
Dispose of resources properly: Use the
using
statement for objects that implementIDisposable
likeStreamReader
andStreamWriter
. -
Check file existence: Before performing operations, verify if files exist where appropriate.
-
Use Path methods for path operations: Always use
Path.Combine()
instead of string concatenation for paths. -
Consider async operations for large files: Use the async versions of file operations (
ReadAllTextAsync
, etc.) for better performance. -
Be careful with file permissions: Always ensure your application has appropriate permissions.
-
Handle file locks: Be prepared to handle scenarios where files are locked by other processes.
-
Use relative paths when appropriate: This makes your application more portable.
Summary
In this tutorial, we've covered the essential file operations in C# including:
- Checking if files exist
- Creating files
- Reading from files using different approaches
- Writing to files and appending content
- Copying and moving files
- Deleting files
- Getting detailed file information
- Real-world examples with logging and backup systems
File operations are a fundamental part of many applications. C# provides a rich set of tools through the System.IO
namespace that make it straightforward to work with files in a safe and efficient manner.
Exercises
To practice your file handling skills, try these exercises:
- Create a simple text editor application that can open, edit, and save text files.
- Implement a file merger that combines multiple text files into a single file.
- Create a file monitoring tool that watches a directory and logs any file changes.
- Build a CSV file processor that can read, modify, and write CSV data.
- Implement a file encryption/decryption utility using simple XOR encryption.
Additional Resources
- Microsoft Docs: File and Stream I/O
- C# FileInfo Class
- C# File Class
- Best Practices for Using Strings in .NET
Remember that file operations should always be performed with proper exception handling and security considerations in mind. Happy coding!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)