4.8 - File and Directory Operations
Working with files and directories is a common requirement in many applications. C# provides several classes in the System.IO
namespace for performing file and directory operations. This chapter explores these classes and their capabilities.
4.8.1 - File I/O Basics
The File
and FileInfo
classes provide methods for creating, copying, deleting, moving, and opening files.
4.8.1.1 - File vs. FileInfo
The File
class provides static methods for file operations, while the FileInfo
class provides instance methods:
Example:
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "example.txt";
// Using the File class (static methods)
// Create a file and write some text
File.WriteAllText(filePath, "Hello, World!");
// Check if the file exists
bool exists = File.Exists(filePath);
Console.WriteLine($"File exists: {exists}");
// Get file information
DateTime creationTime = File.GetCreationTime(filePath);
DateTime lastAccessTime = File.GetLastAccessTime(filePath);
DateTime lastWriteTime = File.GetLastWriteTime(filePath);
long fileSize = new FileInfo(filePath).Length;
Console.WriteLine($"Creation time: {creationTime}");
Console.WriteLine($"Last access time: {lastAccessTime}");
Console.WriteLine($"Last write time: {lastWriteTime}");
Console.WriteLine($"File size: {fileSize} bytes");
// Using the FileInfo class (instance methods)
// Create a FileInfo object
FileInfo fileInfo = new FileInfo(filePath);
// Get file information
Console.WriteLine($"\nFile name: {fileInfo.Name}");
Console.WriteLine($"Full path: {fileInfo.FullName}");
Console.WriteLine($"Directory: {fileInfo.DirectoryName}");
Console.WriteLine($"Extension: {fileInfo.Extension}");
Console.WriteLine($"Size: {fileInfo.Length} bytes");
Console.WriteLine($"Creation time: {fileInfo.CreationTime}");
Console.WriteLine($"Last access time: {fileInfo.LastAccessTime}");
Console.WriteLine($"Last write time: {fileInfo.LastWriteTime}");
Console.WriteLine($"Is read-only: {fileInfo.IsReadOnly}");
// Copy the file
string copyPath = "example_copy.txt";
fileInfo.CopyTo(copyPath, true);
Console.WriteLine($"\nFile copied to: {copyPath}");
// Delete the files
File.Delete(filePath);
File.Delete(copyPath);
Console.WriteLine("Files deleted");
}
}
4.8.1.2 - File Attributes
The FileAttributes
enumeration represents attributes of a file or directory:
Example:
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "example.txt";
// Create a file
File.WriteAllText(filePath, "Hello, World!");
// Get file attributes
FileAttributes attributes = File.GetAttributes(filePath);
Console.WriteLine($"File attributes: {attributes}");
// Check specific attributes
bool isReadOnly = (attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
bool isHidden = (attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
bool isSystem = (attributes & FileAttributes.System) == FileAttributes.System;
bool isArchive = (attributes & FileAttributes.Archive) == FileAttributes.Archive;
Console.WriteLine($"Is read-only: {isReadOnly}");
Console.WriteLine($"Is hidden: {isHidden}");
Console.WriteLine($"Is system: {isSystem}");
Console.WriteLine($"Is archive: {isArchive}");
// Set file attributes
File.SetAttributes(filePath, FileAttributes.ReadOnly);
Console.WriteLine("\nSet file to read-only");
// Get updated attributes
attributes = File.GetAttributes(filePath);
Console.WriteLine($"Updated attributes: {attributes}");
// Remove read-only attribute
File.SetAttributes(filePath, attributes & ~FileAttributes.ReadOnly);
Console.WriteLine("Removed read-only attribute");
// Get updated attributes
attributes = File.GetAttributes(filePath);
Console.WriteLine($"Final attributes: {attributes}");
// Delete the file
File.Delete(filePath);
Console.WriteLine("File deleted");
}
}
4.8.1.3 - File Streams
The FileStream
class provides a stream for reading from and writing to files:
Example:
using System;
using System.IO;
using System.Text;
class Program
{
static void Main()
{
string filePath = "stream_example.txt";
// Write to a file using FileStream
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
string text = "Hello, FileStream!";
byte[] bytes = Encoding.UTF8.GetBytes(text);
fs.Write(bytes, 0, bytes.Length);
Console.WriteLine($"Wrote {bytes.Length} bytes to the file");
}
// Read from a file using FileStream
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
byte[] bytes = new byte[fs.Length];
int bytesRead = fs.Read(bytes, 0, bytes.Length);
string text = Encoding.UTF8.GetString(bytes);
Console.WriteLine($"Read {bytesRead} bytes from the file");
Console.WriteLine($"Content: {text}");
}
// Append to a file using FileStream
using (FileStream fs = new FileStream(filePath, FileMode.Append, FileAccess.Write))
{
string additionalText = "\nThis text is appended.";
byte[] bytes = Encoding.UTF8.GetBytes(additionalText);
fs.Write(bytes, 0, bytes.Length);
Console.WriteLine($"Appended {bytes.Length} bytes to the file");
}
// Read the updated file
string content = File.ReadAllText(filePath);
Console.WriteLine($"\nUpdated content:\n{content}");
// Delete the file
File.Delete(filePath);
Console.WriteLine("File deleted");
}
}
4.8.2 - Reading and Writing Text Files
C# provides several methods for reading and writing text files.
4.8.2.1 - Reading Text Files
Methods for reading text files:
Example:
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "text_example.txt";
// Create a sample text file
string[] lines = {
"Line 1: This is the first line.",
"Line 2: This is the second line.",
"Line 3: This is the third line.",
"Line 4: This is the fourth line.",
"Line 5: This is the fifth line."
};
File.WriteAllLines(filePath, lines);
Console.WriteLine("Sample text file created");
// Read all text at once
string allText = File.ReadAllText(filePath);
Console.WriteLine("\nReading all text at once:");
Console.WriteLine(allText);
// Read all lines at once
string[] allLines = File.ReadAllLines(filePath);
Console.WriteLine("\nReading all lines at once:");
foreach (string line in allLines)
{
Console.WriteLine($" {line}");
}
// Read file line by line
Console.WriteLine("\nReading file line by line:");
using (StreamReader reader = new StreamReader(filePath))
{
string line;
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine($" {line}");
}
}
// Read file character by character
Console.WriteLine("\nReading file character by character:");
using (StreamReader reader = new StreamReader(filePath))
{
int charCount = 0;
int charValue;
while ((charValue = reader.Read()) != -1)
{
char c = (char)charValue;
charCount++;
// Just count characters, don't print them all
if (charCount <= 20)
{
Console.Write(c);
}
}
Console.WriteLine($"\nTotal characters: {charCount}");
}
// Delete the file
File.Delete(filePath);
Console.WriteLine("\nFile deleted");
}
}
4.8.2.2 - Writing Text Files
Methods for writing text files:
Example:
using System;
using System.IO;
class Program
{
static void Main()
{
// Write all text at once
string filePath1 = "write_all_text.txt";
string content = "This is a sample text.\nIt has multiple lines.\nThis is the third line.";
File.WriteAllText(filePath1, content);
Console.WriteLine($"Text written to {filePath1}");
// Write all lines at once
string filePath2 = "write_all_lines.txt";
string[] lines = {
"Line 1: This is the first line.",
"Line 2: This is the second line.",
"Line 3: This is the third line."
};
File.WriteAllLines(filePath2, lines);
Console.WriteLine($"Lines written to {filePath2}");
// Write using StreamWriter
string filePath3 = "stream_writer.txt";
using (StreamWriter writer = new StreamWriter(filePath3))
{
writer.WriteLine("This is the first line written with StreamWriter.");
writer.WriteLine("This is the second line.");
writer.Write("This is on the third line, ");
writer.WriteLine("still on the third line.");
// Write without a newline
writer.Write("This is on the fourth line.");
}
Console.WriteLine($"Text written to {filePath3} using StreamWriter");
// Append text to an existing file
using (StreamWriter writer = new StreamWriter(filePath3, true))
{
writer.WriteLine(" This text is appended.");
writer.WriteLine("This is another appended line.");
}
Console.WriteLine($"Text appended to {filePath3}");
// Read and display the content of the appended file
string appendedContent = File.ReadAllText(filePath3);
Console.WriteLine($"\nContent of {filePath3}:\n{appendedContent}");
// Delete the files
File.Delete(filePath1);
File.Delete(filePath2);
File.Delete(filePath3);
Console.WriteLine("\nFiles deleted");
}
}
4.8.2.3 - Working with Encodings
Text files can be read and written with different encodings:
Example:
using System;
using System.IO;
using System.Text;
class Program
{
static void Main()
{
string filePath = "encoding_example.txt";
string content = "Hello, World! 你好,世界! Привет, мир!";
// Write with different encodings
// UTF-8
File.WriteAllText(filePath, content, Encoding.UTF8);
Console.WriteLine("Text written with UTF-8 encoding");
// Read the UTF-8 file
string utf8Content = File.ReadAllText(filePath, Encoding.UTF8);
Console.WriteLine($"UTF-8 content: {utf8Content}");
// UTF-16 (Unicode)
File.WriteAllText(filePath, content, Encoding.Unicode);
Console.WriteLine("\nText written with UTF-16 encoding");
// Read the UTF-16 file
string utf16Content = File.ReadAllText(filePath, Encoding.Unicode);
Console.WriteLine($"UTF-16 content: {utf16Content}");
// ASCII (will lose non-ASCII characters)
File.WriteAllText(filePath, content, Encoding.ASCII);
Console.WriteLine("\nText written with ASCII encoding");
// Read the ASCII file
string asciiContent = File.ReadAllText(filePath, Encoding.ASCII);
Console.WriteLine($"ASCII content: {asciiContent}");
// Using StreamWriter with encoding
using (StreamWriter writer = new StreamWriter(filePath, false, Encoding.UTF8))
{
writer.WriteLine(content);
}
Console.WriteLine("\nText written with StreamWriter using UTF-8 encoding");
// Using StreamReader with encoding
using (StreamReader reader = new StreamReader(filePath, Encoding.UTF8))
{
string streamContent = reader.ReadToEnd();
Console.WriteLine($"StreamReader content: {streamContent}");
}
// Delete the file
File.Delete(filePath);
Console.WriteLine("\nFile deleted");
}
}
4.8.3 - Binary File Operations
Binary files contain data in a format that is not readable as text.
4.8.3.1 - Reading and Writing Binary Files
Methods for reading and writing binary files:
Example:
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "binary_example.bin";
// Write binary data
byte[] dataToWrite = { 0x48, 0x65, 0x6C, 0x6C, 0x6F }; // "Hello" in ASCII
File.WriteAllBytes(filePath, dataToWrite);
Console.WriteLine($"Binary data written to {filePath}");
// Read binary data
byte[] dataRead = File.ReadAllBytes(filePath);
Console.WriteLine("\nBinary data read:");
foreach (byte b in dataRead)
{
Console.Write($"{b:X2} "); // Display as hexadecimal
}
Console.WriteLine();
// Convert to ASCII string
string asciiString = System.Text.Encoding.ASCII.GetString(dataRead);
Console.WriteLine($"ASCII string: {asciiString}");
// Using BinaryWriter and BinaryReader
using (BinaryWriter writer = new BinaryWriter(File.Open(filePath, FileMode.Create)))
{
// Write different types of data
writer.Write(42); // Int32
writer.Write(3.14159); // Double
writer.Write(true); // Boolean
writer.Write("Hello, BinaryWriter!"); // String
writer.Write(new char[] { 'A', 'B', 'C' }); // Char array
}
Console.WriteLine("\nData written using BinaryWriter");
// Read the data using BinaryReader
using (BinaryReader reader = new BinaryReader(File.Open(filePath, FileMode.Open)))
{
int intValue = reader.ReadInt32();
double doubleValue = reader.ReadDouble();
bool boolValue = reader.ReadBoolean();
string stringValue = reader.ReadString();
char[] charArray = reader.ReadChars(3);
Console.WriteLine("\nData read using BinaryReader:");
Console.WriteLine($"Int32: {intValue}");
Console.WriteLine($"Double: {doubleValue}");
Console.WriteLine($"Boolean: {boolValue}");
Console.WriteLine($"String: {stringValue}");
Console.WriteLine($"Char array: {new string(charArray)}");
}
// Delete the file
File.Delete(filePath);
Console.WriteLine("\nFile deleted");
}
}
4.8.3.2 - Working with Memory Streams
The MemoryStream
class provides a stream that uses memory as its backing store:
Example:
using System;
using System.IO;
using System.Text;
class Program
{
static void Main()
{
// Create a MemoryStream
using (MemoryStream memoryStream = new MemoryStream())
{
// Write data to the memory stream
byte[] data = Encoding.UTF8.GetBytes("Hello, MemoryStream!");
memoryStream.Write(data, 0, data.Length);
Console.WriteLine($"Data written to MemoryStream: {data.Length} bytes");
// Get the current position
Console.WriteLine($"Current position: {memoryStream.Position}");
// Reset the position to the beginning
memoryStream.Position = 0;
// Read data from the memory stream
byte[] readData = new byte[memoryStream.Length];
int bytesRead = memoryStream.Read(readData, 0, readData.Length);
string readString = Encoding.UTF8.GetString(readData);
Console.WriteLine($"Read {bytesRead} bytes: {readString}");
// Convert MemoryStream to byte array
byte[] streamBytes = memoryStream.ToArray();
Console.WriteLine($"MemoryStream as byte array: {streamBytes.Length} bytes");
// Write to a file
string filePath = "memory_stream.txt";
File.WriteAllBytes(filePath, streamBytes);
Console.WriteLine($"MemoryStream content written to {filePath}");
// Read from a file into a MemoryStream
byte[] fileBytes = File.ReadAllBytes(filePath);
using (MemoryStream fileMemoryStream = new MemoryStream(fileBytes))
{
// Read from the new memory stream
byte[] newReadData = new byte[fileMemoryStream.Length];
fileMemoryStream.Read(newReadData, 0, newReadData.Length);
string newReadString = Encoding.UTF8.GetString(newReadData);
Console.WriteLine($"Read from file MemoryStream: {newReadString}");
}
// Delete the file
File.Delete(filePath);
Console.WriteLine("File deleted");
}
}
}
4.8.3.3 - Serialization
Serialization is the process of converting objects to a format that can be stored or transmitted:
Example:
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using System.Text.Json;
[Serializable]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public DateTime BirthDate { get; set; }
// Default constructor required for XML serialization
public Person() { }
public Person(string name, int age, DateTime birthDate)
{
Name = name;
Age = age;
BirthDate = birthDate;
}
public override string ToString()
{
return $"Name: {Name}, Age: {Age}, Birth Date: {BirthDate:d}";
}
}
class Program
{
static void Main()
{
// Create a Person object
Person person = new Person("John Doe", 30, new DateTime(1993, 5, 15));
Console.WriteLine($"Original person: {person}");
// Binary serialization
string binaryFilePath = "person.bin";
SerializeToBinary(person, binaryFilePath);
Person binaryDeserialized = DeserializeFromBinary(binaryFilePath);
Console.WriteLine($"\nBinary deserialized: {binaryDeserialized}");
// XML serialization
string xmlFilePath = "person.xml";
SerializeToXml(person, xmlFilePath);
Person xmlDeserialized = DeserializeFromXml(xmlFilePath);
Console.WriteLine($"\nXML deserialized: {xmlDeserialized}");
// JSON serialization
string jsonFilePath = "person.json";
SerializeToJson(person, jsonFilePath);
Person jsonDeserialized = DeserializeFromJson(jsonFilePath);
Console.WriteLine($"\nJSON deserialized: {jsonDeserialized}");
// Delete the files
File.Delete(binaryFilePath);
File.Delete(xmlFilePath);
File.Delete(jsonFilePath);
Console.WriteLine("\nFiles deleted");
}
// Binary serialization
static void SerializeToBinary(Person person, string filePath)
{
using (FileStream stream = new FileStream(filePath, FileMode.Create))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, person);
}
Console.WriteLine($"Binary serialized to {filePath}");
}
static Person DeserializeFromBinary(string filePath)
{
using (FileStream stream = new FileStream(filePath, FileMode.Open))
{
BinaryFormatter formatter = new BinaryFormatter();
return (Person)formatter.Deserialize(stream);
}
}
// XML serialization
static void SerializeToXml(Person person, string filePath)
{
using (FileStream stream = new FileStream(filePath, FileMode.Create))
{
XmlSerializer serializer = new XmlSerializer(typeof(Person));
serializer.Serialize(stream, person);
}
Console.WriteLine($"XML serialized to {filePath}");
// Display the XML content
string xmlContent = File.ReadAllText(filePath);
Console.WriteLine($"XML content:\n{xmlContent}");
}
static Person DeserializeFromXml(string filePath)
{
using (FileStream stream = new FileStream(filePath, FileMode.Open))
{
XmlSerializer serializer = new XmlSerializer(typeof(Person));
return (Person)serializer.Deserialize(stream);
}
}
// JSON serialization
static void SerializeToJson(Person person, string filePath)
{
string jsonString = JsonSerializer.Serialize(person, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(filePath, jsonString);
Console.WriteLine($"JSON serialized to {filePath}");
// Display the JSON content
Console.WriteLine($"JSON content:\n{jsonString}");
}
static Person DeserializeFromJson(string filePath)
{
string jsonString = File.ReadAllText(filePath);
return JsonSerializer.Deserialize<Person>(jsonString);
}
}
4.8.4 - Directory Management
The Directory
and DirectoryInfo
classes provide methods for creating, moving, and enumerating through directories and subdirectories.
4.8.4.1 - Directory vs. DirectoryInfo
The Directory
class provides static methods for directory operations, while the DirectoryInfo
class provides instance methods:
Example:
using System;
using System.IO;
class Program
{
static void Main()
{
string directoryPath = "example_directory";
// Using the Directory class (static methods)
// Create a directory
Directory.CreateDirectory(directoryPath);
Console.WriteLine($"Directory created: {directoryPath}");
// Check if the directory exists
bool exists = Directory.Exists(directoryPath);
Console.WriteLine($"Directory exists: {exists}");
// Create some files in the directory
File.WriteAllText(Path.Combine(directoryPath, "file1.txt"), "Content of file 1");
File.WriteAllText(Path.Combine(directoryPath, "file2.txt"), "Content of file 2");
// Create a subdirectory
string subdirectoryPath = Path.Combine(directoryPath, "subdirectory");
Directory.CreateDirectory(subdirectoryPath);
File.WriteAllText(Path.Combine(subdirectoryPath, "file3.txt"), "Content of file 3");
// Get directory information
DateTime creationTime = Directory.GetCreationTime(directoryPath);
DateTime lastAccessTime = Directory.GetLastAccessTime(directoryPath);
DateTime lastWriteTime = Directory.GetLastWriteTime(directoryPath);
Console.WriteLine($"Creation time: {creationTime}");
Console.WriteLine($"Last access time: {lastAccessTime}");
Console.WriteLine($"Last write time: {lastWriteTime}");
// Get files in the directory
string[] files = Directory.GetFiles(directoryPath);
Console.WriteLine("\nFiles in the directory:");
foreach (string file in files)
{
Console.WriteLine($" {file}");
}
// Get subdirectories
string[] subdirectories = Directory.GetDirectories(directoryPath);
Console.WriteLine("\nSubdirectories:");
foreach (string subdirectory in subdirectories)
{
Console.WriteLine($" {subdirectory}");
}
// Using the DirectoryInfo class (instance methods)
// Create a DirectoryInfo object
DirectoryInfo directoryInfo = new DirectoryInfo(directoryPath);
// Get directory information
Console.WriteLine($"\nDirectory name: {directoryInfo.Name}");
Console.WriteLine($"Full path: {directoryInfo.FullName}");
Console.WriteLine($"Parent directory: {directoryInfo.Parent?.FullName}");
Console.WriteLine($"Root: {directoryInfo.Root}");
Console.WriteLine($"Creation time: {directoryInfo.CreationTime}");
Console.WriteLine($"Last access time: {directoryInfo.LastAccessTime}");
Console.WriteLine($"Last write time: {directoryInfo.LastWriteTime}");
// Get files using DirectoryInfo
FileInfo[] fileInfos = directoryInfo.GetFiles();
Console.WriteLine("\nFiles (using DirectoryInfo):");
foreach (FileInfo fileInfo in fileInfos)
{
Console.WriteLine($" {fileInfo.Name} - {fileInfo.Length} bytes");
}
// Get subdirectories using DirectoryInfo
DirectoryInfo[] subdirectoryInfos = directoryInfo.GetDirectories();
Console.WriteLine("\nSubdirectories (using DirectoryInfo):");
foreach (DirectoryInfo subdirectoryInfo in subdirectoryInfos)
{
Console.WriteLine($" {subdirectoryInfo.Name}");
}
// Delete the directory and its contents
Directory.Delete(directoryPath, true);
Console.WriteLine("\nDirectory and its contents deleted");
}
}
4.8.4.2 - Recursive Directory Operations
Recursively process directories and their contents:
Example:
using System;
using System.IO;
class Program
{
static void Main()
{
// Create a directory structure for testing
string rootDirectory = "test_directory";
CreateTestDirectoryStructure(rootDirectory);
// Display the directory structure
Console.WriteLine("Directory structure:");
DisplayDirectoryStructure(rootDirectory, 0);
// Count files and directories
int fileCount = 0;
int directoryCount = 0;
CountFilesAndDirectories(rootDirectory, ref fileCount, ref directoryCount);
Console.WriteLine($"\nTotal files: {fileCount}");
Console.WriteLine($"Total directories: {directoryCount}");
// Calculate total size of all files
long totalSize = CalculateTotalSize(rootDirectory);
Console.WriteLine($"Total size: {totalSize} bytes");
// Find all text files
Console.WriteLine("\nText files:");
FindFilesByExtension(rootDirectory, ".txt");
// Delete the test directory structure
Directory.Delete(rootDirectory, true);
Console.WriteLine("\nTest directory structure deleted");
}
// Create a test directory structure
static void CreateTestDirectoryStructure(string rootDirectory)
{
// Create the root directory
Directory.CreateDirectory(rootDirectory);
// Create files in the root directory
File.WriteAllText(Path.Combine(rootDirectory, "root_file1.txt"), "Content of root file 1");
File.WriteAllText(Path.Combine(rootDirectory, "root_file2.dat"), "Content of root file 2");
// Create subdirectory 1
string subdir1 = Path.Combine(rootDirectory, "subdir1");
Directory.CreateDirectory(subdir1);
// Create files in subdirectory 1
File.WriteAllText(Path.Combine(subdir1, "subdir1_file1.txt"), "Content of subdir1 file 1");
File.WriteAllText(Path.Combine(subdir1, "subdir1_file2.txt"), "Content of subdir1 file 2");
// Create subdirectory 2
string subdir2 = Path.Combine(rootDirectory, "subdir2");
Directory.CreateDirectory(subdir2);
// Create files in subdirectory 2
File.WriteAllText(Path.Combine(subdir2, "subdir2_file1.dat"), "Content of subdir2 file 1");
// Create a nested subdirectory
string nestedSubdir = Path.Combine(subdir2, "nested");
Directory.CreateDirectory(nestedSubdir);
// Create files in the nested subdirectory
File.WriteAllText(Path.Combine(nestedSubdir, "nested_file1.txt"), "Content of nested file 1");
Console.WriteLine("Test directory structure created");
}
// Display the directory structure with indentation
static void DisplayDirectoryStructure(string directory, int level)
{
// Display the current directory
string indent = new string(' ', level * 2);
Console.WriteLine($"{indent}[{Path.GetFileName(directory)}]");
// Display files in the current directory
foreach (string file in Directory.GetFiles(directory))
{
Console.WriteLine($"{indent} {Path.GetFileName(file)}");
}
// Recursively display subdirectories
foreach (string subdirectory in Directory.GetDirectories(directory))
{
DisplayDirectoryStructure(subdirectory, level + 1);
}
}
// Count files and directories recursively
static void CountFilesAndDirectories(string directory, ref int fileCount, ref int directoryCount)
{
// Count files in the current directory
fileCount += Directory.GetFiles(directory).Length;
// Count and process subdirectories
string[] subdirectories = Directory.GetDirectories(directory);
directoryCount += subdirectories.Length;
foreach (string subdirectory in subdirectories)
{
CountFilesAndDirectories(subdirectory, ref fileCount, ref directoryCount);
}
}
// Calculate the total size of all files recursively
static long CalculateTotalSize(string directory)
{
long totalSize = 0;
// Add sizes of files in the current directory
foreach (string file in Directory.GetFiles(directory))
{
FileInfo fileInfo = new FileInfo(file);
totalSize += fileInfo.Length;
}
// Recursively add sizes of files in subdirectories
foreach (string subdirectory in Directory.GetDirectories(directory))
{
totalSize += CalculateTotalSize(subdirectory);
}
return totalSize;
}
// Find files with a specific extension recursively
static void FindFilesByExtension(string directory, string extension)
{
// Find files with the specified extension in the current directory
foreach (string file in Directory.GetFiles(directory, $"*{extension}"))
{
Console.WriteLine($" {file}");
}
// Recursively search subdirectories
foreach (string subdirectory in Directory.GetDirectories(directory))
{
FindFilesByExtension(subdirectory, extension);
}
}
}
4.8.5 - File System Watchers
The FileSystemWatcher
class allows you to monitor a directory for changes to files and subdirectories.
4.8.5.1 - Monitoring File System Changes
Monitor a directory for changes:
Example:
using System;
using System.IO;
using System.Threading;
class Program
{
static void Main()
{
// Create a directory to monitor
string directoryToMonitor = "monitored_directory";
Directory.CreateDirectory(directoryToMonitor);
// Create a FileSystemWatcher
using (FileSystemWatcher watcher = new FileSystemWatcher(directoryToMonitor))
{
// Set properties
watcher.NotifyFilter = NotifyFilters.FileName
| NotifyFilters.DirectoryName
| NotifyFilters.LastWrite
| NotifyFilters.CreationTime;
// Watch for all changes
watcher.Filter = "*.*";
watcher.IncludeSubdirectories = true;
// Add event handlers
watcher.Created += OnChanged;
watcher.Deleted += OnChanged;
watcher.Renamed += OnRenamed;
watcher.Changed += OnChanged;
// Start monitoring
watcher.EnableRaisingEvents = true;
Console.WriteLine($"Monitoring directory: {directoryToMonitor}");
Console.WriteLine("Press 'q' to quit the sample.");
// Perform some file operations
Thread.Sleep(1000); // Wait a bit before starting operations
// Create a file
string filePath = Path.Combine(directoryToMonitor, "test_file.txt");
File.WriteAllText(filePath, "Initial content");
Thread.Sleep(1000);
// Modify the file
File.AppendAllText(filePath, "\nAdditional content");
Thread.Sleep(1000);
// Rename the file
string newFilePath = Path.Combine(directoryToMonitor, "renamed_file.txt");
File.Move(filePath, newFilePath);
Thread.Sleep(1000);
// Create a subdirectory
string subdirectoryPath = Path.Combine(directoryToMonitor, "subdirectory");
Directory.CreateDirectory(subdirectoryPath);
Thread.Sleep(1000);
// Create a file in the subdirectory
string subfilePath = Path.Combine(subdirectoryPath, "subfile.txt");
File.WriteAllText(subfilePath, "Subdirectory file content");
Thread.Sleep(1000);
// Delete the file
File.Delete(newFilePath);
Thread.Sleep(1000);
// Delete the subdirectory and its contents
Directory.Delete(subdirectoryPath, true);
// Wait for user input
while (Console.Read() != 'q') ;
}
// Clean up
Directory.Delete(directoryToMonitor, true);
}
// Event handlers
private static void OnChanged(object sender, FileSystemEventArgs e)
{
Console.WriteLine($"{DateTime.Now} - {e.ChangeType}: {e.FullPath}");
}
private static void OnRenamed(object sender, RenamedEventArgs e)
{
Console.WriteLine($"{DateTime.Now} - {e.ChangeType}: {e.OldFullPath} to {e.FullPath}");
}
}
4.8.5.2 - Practical Applications
Practical applications of FileSystemWatcher
:
Example:
using System;
using System.IO;
using System.Threading;
class Program
{
static void Main()
{
// Create directories
string sourceDirectory = "source_directory";
string backupDirectory = "backup_directory";
string processedDirectory = "processed_directory";
Directory.CreateDirectory(sourceDirectory);
Directory.CreateDirectory(backupDirectory);
Directory.CreateDirectory(processedDirectory);
// Create a FileSystemWatcher for the source directory
using (FileSystemWatcher watcher = new FileSystemWatcher(sourceDirectory))
{
// Set properties
watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite;
watcher.Filter = "*.txt";
// Add event handlers
watcher.Created += (sender, e) => ProcessNewFile(e.FullPath, backupDirectory, processedDirectory);
// Start monitoring
watcher.EnableRaisingEvents = true;
Console.WriteLine($"Monitoring directory: {sourceDirectory}");
Console.WriteLine("Press 'q' to quit the sample.");
// Create some test files
Thread.Sleep(1000);
for (int i = 1; i <= 3; i++)
{
string filePath = Path.Combine(sourceDirectory, $"test_file{i}.txt");
File.WriteAllText(filePath, $"Content of test file {i}");
Thread.Sleep(1000);
}
// Wait for user input
while (Console.Read() != 'q') ;
}
// Clean up
Directory.Delete(sourceDirectory, true);
Directory.Delete(backupDirectory, true);
Directory.Delete(processedDirectory, true);
}
// Process a new file
private static void ProcessNewFile(string filePath, string backupDirectory, string processedDirectory)
{
try
{
Console.WriteLine($"Processing file: {filePath}");
// Wait a moment to ensure the file is fully written
Thread.Sleep(500);
// Read the file content
string content = File.ReadAllText(filePath);
// Create a backup
string fileName = Path.GetFileName(filePath);
string backupPath = Path.Combine(backupDirectory, fileName);
File.Copy(filePath, backupPath, true);
Console.WriteLine($"Backup created: {backupPath}");
// Process the file (in this example, convert to uppercase)
string processedContent = content.ToUpper();
// Write the processed content
string processedPath = Path.Combine(processedDirectory, fileName);
File.WriteAllText(processedPath, processedContent);
Console.WriteLine($"Processed file created: {processedPath}");
// Delete the original file
File.Delete(filePath);
Console.WriteLine($"Original file deleted: {filePath}");
}
catch (Exception ex)
{
Console.WriteLine($"Error processing file {filePath}: {ex.Message}");
}
}
}
4.8.6 - Path Operations
The Path
class provides methods for working with file and directory paths.
4.8.6.1 - Path Manipulation
Methods for manipulating file and directory paths:
Example:
using System;
using System.IO;
class Program
{
static void Main()
{
// Example path
string filePath = @"C:\Users\Username\Documents\example.txt";
// Get the file name
string fileName = Path.GetFileName(filePath);
Console.WriteLine($"File name: {fileName}");
// Get the file name without extension
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath);
Console.WriteLine($"File name without extension: {fileNameWithoutExtension}");
// Get the file extension
string extension = Path.GetExtension(filePath);
Console.WriteLine($"Extension: {extension}");
// Get the directory name
string directoryName = Path.GetDirectoryName(filePath);
Console.WriteLine($"Directory name: {directoryName}");
// Get the root directory
string rootDirectory = Path.GetPathRoot(filePath);
Console.WriteLine($"Root directory: {rootDirectory}");
// Combine paths
string newPath = Path.Combine(directoryName, "newfile.txt");
Console.WriteLine($"Combined path: {newPath}");
// Change the extension
string pathWithNewExtension = Path.ChangeExtension(filePath, ".docx");
Console.WriteLine($"Path with new extension: {pathWithNewExtension}");
// Get a temporary file name
string tempFileName = Path.GetTempFileName();
Console.WriteLine($"Temporary file name: {tempFileName}");
// Get the temporary directory
string tempDirectory = Path.GetTempPath();
Console.WriteLine($"Temporary directory: {tempDirectory}");
// Get a random file name
string randomFileName = Path.GetRandomFileName();
Console.WriteLine($"Random file name: {randomFileName}");
// Check if a path has an extension
bool hasExtension = Path.HasExtension(filePath);
Console.WriteLine($"Has extension: {hasExtension}");
// Get the full path
string relativePath = @"Documents\example.txt";
string fullPath = Path.GetFullPath(relativePath);
Console.WriteLine($"Full path: {fullPath}");
// Clean up
if (File.Exists(tempFileName))
{
File.Delete(tempFileName);
}
}
}
4.8.6.2 - Path Validation
Methods for validating file and directory paths:
Example:
using System;
using System.IO;
class Program
{
static void Main()
{
// Valid and invalid paths
string[] paths = {
@"C:\Users\Username\Documents\example.txt",
@"C:\Users\Username\Documents\folder\",
@"invalid:path",
@"C:\Users\Username\Documents\file?.txt",
@"\\server\share\file.txt",
@"http://example.com/file.txt"
};
foreach (string path in paths)
{
Console.WriteLine($"Path: {path}");
// Check if the path is valid
bool isValid = IsValidPath(path);
Console.WriteLine($" Is valid path: {isValid}");
if (isValid)
{
// Get path components
Console.WriteLine($" Directory: {Path.GetDirectoryName(path)}");
Console.WriteLine($" File name: {Path.GetFileName(path)}");
Console.WriteLine($" Extension: {Path.GetExtension(path)}");
}
Console.WriteLine();
}
// Check for invalid characters
char[] invalidChars = Path.GetInvalidPathChars();
Console.WriteLine("Invalid path characters:");
foreach (char c in invalidChars)
{
Console.Write($"\\u{(int)c:X4} ");
}
Console.WriteLine();
char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
Console.WriteLine("\nInvalid file name characters:");
foreach (char c in invalidFileNameChars)
{
Console.Write($"\\u{(int)c:X4} ");
}
Console.WriteLine();
}
// Check if a path is valid
static bool IsValidPath(string path)
{
try
{
// Check for invalid characters
if (path.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
{
return false;
}
// Try to get the full path
string fullPath = Path.GetFullPath(path);
// Additional checks can be added here
return true;
}
catch (Exception)
{
return false;
}
}
}
4.8.7 - Async File I/O
Asynchronous file I/O operations allow you to perform file operations without blocking the main thread.
4.8.7.1 - Reading and Writing Files Asynchronously
Methods for reading and writing files asynchronously:
Example:
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
string filePath = "async_example.txt";
// Write to a file asynchronously
await WriteFileAsync(filePath, "This is line 1\nThis is line 2\nThis is line 3");
Console.WriteLine("File written asynchronously");
// Read the file asynchronously
string content = await ReadFileAsync(filePath);
Console.WriteLine("\nFile content read asynchronously:");
Console.WriteLine(content);
// Append to the file asynchronously
await AppendToFileAsync(filePath, "\nThis line was appended asynchronously");
Console.WriteLine("\nContent appended asynchronously");
// Read the updated file
content = await ReadFileAsync(filePath);
Console.WriteLine("\nUpdated file content:");
Console.WriteLine(content);
// Read the file line by line asynchronously
Console.WriteLine("\nReading file line by line asynchronously:");
await ReadLinesAsync(filePath);
// Copy the file asynchronously
string copyPath = "async_copy.txt";
await CopyFileAsync(filePath, copyPath);
Console.WriteLine($"\nFile copied asynchronously to {copyPath}");
// Delete the files
File.Delete(filePath);
File.Delete(copyPath);
Console.WriteLine("\nFiles deleted");
}
// Write to a file asynchronously
static async Task WriteFileAsync(string filePath, string content)
{
byte[] encodedText = Encoding.UTF8.GetBytes(content);
using (FileStream sourceStream = new FileStream(filePath,
FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true))
{
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}
}
// Read a file asynchronously
static async Task<string> ReadFileAsync(string filePath)
{
using (FileStream sourceStream = new FileStream(filePath,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096, useAsync: true))
{
StringBuilder sb = new StringBuilder();
byte[] buffer = new byte[0x1000];
int numRead;
while ((numRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
string text = Encoding.UTF8.GetString(buffer, 0, numRead);
sb.Append(text);
}
return sb.ToString();
}
}
// Append to a file asynchronously
static async Task AppendToFileAsync(string filePath, string content)
{
byte[] encodedText = Encoding.UTF8.GetBytes(content);
using (FileStream sourceStream = new FileStream(filePath,
FileMode.Append, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true))
{
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}
}
// Read a file line by line asynchronously
static async Task ReadLinesAsync(string filePath)
{
using (StreamReader reader = new StreamReader(
new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096, useAsync: true)))
{
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
Console.WriteLine($" {line}");
}
}
}
// Copy a file asynchronously
static async Task CopyFileAsync(string sourceFile, string destinationFile)
{
using (FileStream sourceStream = new FileStream(sourceFile,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096, useAsync: true))
{
using (FileStream destinationStream = new FileStream(destinationFile,
FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true))
{
await sourceStream.CopyToAsync(destinationStream);
}
}
}
}
4.8.7.2 - Parallel File Operations
Perform multiple file operations in parallel:
Example:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
// Create a directory for testing
string directoryPath = "parallel_test";
Directory.CreateDirectory(directoryPath);
// Create multiple files
int fileCount = 10;
List<string> filePaths = new List<string>();
for (int i = 1; i <= fileCount; i++)
{
string filePath = Path.Combine(directoryPath, $"file{i}.txt");
filePaths.Add(filePath);
// Create the file with some content
File.WriteAllText(filePath, $"Content of file {i}");
}
Console.WriteLine($"Created {fileCount} files");
// Read all files in parallel
Console.WriteLine("\nReading files in parallel:");
var readTasks = filePaths.Select(path => ReadFileAsync(path));
string[] contents = await Task.WhenAll(readTasks);
for (int i = 0; i < contents.Length; i++)
{
Console.WriteLine($" {Path.GetFileName(filePaths[i])}: {contents[i]}");
}
// Modify all files in parallel
Console.WriteLine("\nModifying files in parallel:");
var modifyTasks = filePaths.Select((path, index) =>
ModifyFileAsync(path, $"Modified content of file {index + 1}"));
await Task.WhenAll(modifyTasks);
// Read the modified files
Console.WriteLine("\nReading modified files:");
foreach (string path in filePaths)
{
string content = await ReadFileAsync(path);
Console.WriteLine($" {Path.GetFileName(path)}: {content}");
}
// Process files in parallel
Console.WriteLine("\nProcessing files in parallel:");
var processTasks = filePaths.Select(path => ProcessFileAsync(path));
string[] results = await Task.WhenAll(processTasks);
for (int i = 0; i < results.Length; i++)
{
Console.WriteLine($" {Path.GetFileName(filePaths[i])}: {results[i]}");
}
// Delete the directory and its contents
Directory.Delete(directoryPath, true);
Console.WriteLine("\nDirectory and files deleted");
}
// Read a file asynchronously
static async Task<string> ReadFileAsync(string filePath)
{
using (StreamReader reader = new StreamReader(
new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096, useAsync: true)))
{
return await reader.ReadToEndAsync();
}
}
// Modify a file asynchronously
static async Task ModifyFileAsync(string filePath, string newContent)
{
using (StreamWriter writer = new StreamWriter(
new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true)))
{
await writer.WriteAsync(newContent);
}
}
// Process a file asynchronously
static async Task<string> ProcessFileAsync(string filePath)
{
// Read the file
string content = await ReadFileAsync(filePath);
// Process the content (in this example, convert to uppercase)
string processedContent = content.ToUpper();
// Write the processed content back to the file
await ModifyFileAsync(filePath, processedContent);
// Return a result
return $"Processed: {content} -> {processedContent}";
}
}
Working with files and directories is a fundamental aspect of many applications. C# provides a rich set of classes and methods for performing file and directory operations, from basic reading and writing to more advanced operations like asynchronous I/O and file system monitoring. By understanding these capabilities, you can effectively manage files and directories in your applications.