Accessing, reading, and writing data to files are all part of C#’s File I/O, or File Input/Output, procedures. Many apps rely on it as a basic feature that allows programs to communicate with external storage. In C#, a CS file (“. cs” file) is a source code file that contains C# code. It is the standard file extension used for C# source code files.
Core Concepts of C# File I/O
Streams: A stream is the fundamental idea behind C# File I/O. From one application or input device to another application or output device, a stream is an ordered sequence of bytes.
- Files become streams when they are opened for reading or writing.
- An input stream, which reads data from a file, and an output stream, which writes data into a file, are the two primary types.
- An abstraction of a data communication channel is called a stream. In order to function, they send and receive bytes sequentially, always in the same order.
- Streams can be used with a variety of backing stores, including memory (MemoryStream), files (FileStream), and network access (NetworkStream).
- While text streams deal with textual information, binary streams deal with raw binary data. A series of text lines, divided by new line separators, is how text streams interpret binary data. Files are seen by operating systems as collections of bytes; how these bytes are handled determines whether they are interpreted as text or binary.
Namespaces: System.IO is C#’s main namespace for handling file and stream operations. Several classes for creating, deleting, reading from, writing to, and closing files are included. Debugging and tracking the execution of applications is another benefit of the System.Diagnostics namespace, which may be pertinent to File I/O.
Key Classes for File I/O
There are numerous non-abstract classes in the System.IO namespace that are frequently used for file operations:
File
Class:
Tasks like creating, copying, moving, removing, and verifying the presence of files are made easier by this static class’s numerous static methods for typical file operations.
File.ReadAllText()
: Used to read a file’s contents into a string in its entirety.File.Create()
: Returns a FileStream object after opening a new file created at a given path.File.Move()
: Carries a file from its source location to its destination.File.Exists()
: Prevents exceptions by verifying the existence of a file before doing any operations.
FileStream
Class:
The abstract Stream class is the ancestor of this class, which is essential for reading from, writing to, and closing files at any place. It permits more precise control over file access. When a FileStream object is created, you provide:
FileMode
: Among the options for opening the file are Open, Create, Append, OpenOrCreate, CreateNew, and Truncate.FileAccess
: Access type allowed (e.g., Read, Write, ReadWrite).FileShare
: How the file is accessible to other processes (e.g., None, Read, ReadWrite, Write) while it is open.
StreamReader
Class:
Uses a byte stream, especially text files, to read text and characters. It inherits from TextReader. It makes reading text files easier, much like reading from the console.
ReadLine()
: Returns a line of characters as a string after reading it from the current stream.Read()
: The subsequent character is read from the input stream.Close()
: System resources are released when the StreamReader and the stream it is based on are closed.
StreamWriter
Class:
Carries over from TextWriter and is used to write text and characters to a stream, usually text files.
Write()
: Adds a value to the stream, such as a string.WriteLine()
: Adds a line of text and a line terminator to the stream.Close()
: Both the underlying stream and the StreamWriter are closed.Flush()
: Buffered data is written to the underlying stream once buffers have been cleared.
BinaryReader
and BinaryWriter
Classes:
These classes are used to read and write binary streams containing primitive data types (such as numbers, characters, booleans, arrays, and strings).
Directory
and DirectoryInfo
Classes:
Used for directory structure manipulation, including listing, removing, relocating, and creating directories and files. Directory, for example.An array of FileInfo objects for the files in a given directory can be returned by GetFiles(). Hierarchical data structures, such as file system directory trees, can be efficiently navigated via recursion.
Path
Class:
Combines paths, retrieves file extensions, and extracts file names, among other actions on path information.
MemoryStream
Class:
Used to provide random access to streamed data that is kept in memory.
FileSystemWatcher
Class:
Permits tracking changes to the file system, including the creation, deletion, modification, and renaming of files.
Error Handling in File I/O
Because file operations can go wrong (file not found, access prohibited, etc.), it is essential to have strong exception handling.
try-catch-finally
Blocks:
To deal with possible exceptions, these are frequently employed.
try
block: Includes the code that could cause an exception.c
atch
block: Allows for distinct replies for various mistakes by catching and managing particular exception types (e.g., FileNotFoundException, IOException, DirectoryNotFoundException, UnauthorizedAccessException). Because there will only be one catch block that runs, it is crucial to mention the most precise exceptions first, followed by their ancestors.finally
block: Often used for resource cleanup, it contains code that is guaranteed to run whether an exception occurred or was handled.
using
Statement:
This C# construct offers a more straightforward and secure method of guaranteeing appropriate resource disposal, particularly for objects that implement the IDisposable interface (such as FileStream, StreamReader, and StreamWriter). When leaving the using block, it makes sure that the Dispose() method which normally calls Close() is invoked automatically, even in the event of an exception.
Code Examples
Writing to a Text File using StreamWriter and using statement:
using System;
using System.IO;
public static class FileWriterExample
{
public static void WriteNumbersToFile(string fileName)
{
// Create a StreamWriter instance
StreamWriter writer = new StreamWriter(fileName);
// Ensure the writer will be closed when no longer used using the 'using' construct
using (writer)
{
// Loop through the numbers from 1 to 20 and write them to the file
for (int i = 1; i <= 20; i++)
{
writer.WriteLine(i); // Writes each number on a new line
}
}
Console.WriteLine($"Numbers 1 to 20 written to {fileName}");
}
public static void Main(string[] args)
{
WriteNumbersToFile("numbers.txt");
}
}
Output
Numbers 1 to 20 written to numbers.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Even in the event of an error, this example makes sure the StreamWriter is terminated correctly.
Reading from a Text File using StreamReader and using statement:
using System;
using System.IO;
using System.Text;
public static class FileWriterExample
{
public static void WriteNumbersToFile(string fileName)
{
try
{
using (StreamWriter writer = new StreamWriter(fileName, false, Encoding.UTF8))
{
writer.WriteLine("This is line 1.");
writer.WriteLine("Line 2 contains some text.");
writer.WriteLine("3. And a final line with a number.");
writer.WriteLine("4. Another test line.");
}
Console.WriteLine($"\nSuccessfully created and wrote to '{fileName}'.");
}
catch (IOException ex)
{
Console.Error.WriteLine($"Error writing to file '{fileName}': {ex.Message}");
}
}
}
public static class FileReaderExample
{
public static void ReadFileContents(string fileName)
{
try
{
using (StreamReader reader = new StreamReader(fileName))
{
int lineNumber = 0;
string line;
while ((line = reader.ReadLine()) != null)
{
lineNumber++;
Console.WriteLine($"Line {lineNumber}: {line}");
}
}
Console.WriteLine($"\nSuccessfully read {fileName}.");
}
catch (FileNotFoundException)
{
Console.Error.WriteLine($"Error: File '{fileName}' not found.");
}
catch (IOException)
{
Console.Error.WriteLine($"Error: Could not read file '{fileName}'.");
}
}
public static void Main(string[] args)
{
FileWriterExample.WriteNumbersToFile("sample.txt");
Console.WriteLine("\n--- Reading 'sample.txt' ---");
ReadFileContents("sample.txt");
Console.WriteLine("\n--- Attempting to read 'nonexistent.txt' ---");
ReadFileContents("nonexistent.txt");
}
}
Output
Successfully created and wrote to 'sample.txt'.
--- Reading 'sample.txt' ---
Line 1: This is line 1.
Line 2: Line 2 contains some text.
Line 3: 3. And a final line with a number.
Line 4: 4. Another test line.
Successfully read sample.txt.
--- Attempting to read 'nonexistent.txt' ---
Error: File 'nonexistent.txt' not found.
This illustrates handling typical file-related exceptions, safe file reading, and appropriate resource closure.
Basic FileStream Usage (Binary):
using System;
using System.IO;
public class FileStreamExample
{
public static void Main(string[] args)
{
FileStream myfile = new FileStream("test.dat", FileMode.OpenOrCreate, FileAccess.ReadWrite);
// Write bytes from 0 to 20
for (int i = 0; i <= 20; i++)
{
myfile.WriteByte((byte)i);
}
// Set position to the beginning of the file to read
myfile.Position = 0;
// Read and print bytes
for (int i = 0; i <= 20; i++)
{
Console.Write(myfile.ReadByte() + " ");
}
myfile.Close(); // Close the FileStream
Console.WriteLine("\nBinary data written and read from test.dat.");
Console.ReadKey();
}
}
Output
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Binary data written and read from test.dat.
Setting the stream’s location and performing low-level byte-by-byte reading and writing are both demonstrated in this example.
Having a digital post office is similar to file I/O in C#. You can transmit your mail using different “stamps” (file modes and access permissions), store it in special “mailboxes” (files or directories), and use different kinds of “envelopes” (streams) for different “messages” (data types like text or binary). In the same way that a successful postal service has protocols for misplaced or damaged items, the system makes sure your “mail” reaches its destination and gracefully manages any “delivery errors” (exceptions).
You can also read Exploring Keywords In C#: What They Are And How They Work