C# provides several ways to open, read from, and write to files on the file system. The key classes that enable file I/O in C# are FileStream, StreamReader, and StreamWriter from the System.IO namespace. In this comprehensive guide, we will explore various examples of opening, reading, writing, and manipulating files in C# using these classes.

The FileStream Class

The FileStream class provides the core functionality for reading from and writing to files. To open a file, you create a FileStream object, specifying the file path and the mode you want to open the file in. Some common file modes are:

  • FileMode.Open: Opens an existing file for reading/writing. An exception is thrown if the file does not exist.
  • FileMode.Create: Creates a new file. An exception is thrown if the file already exists.
  • FileMode.Append: Opens an existing file or creates a new file if it doesn‘t exist, for appending data to the end.
  • FileMode.Truncate: Opens an existing file and truncates its size to 0 bytes to overwrite it.

Here is an example of opening an existing file for reading:

FileStream file = new FileStream("data.txt", FileMode.Open);

Once you have a FileStream, you can call methods like Read() and Write() to access the file contents.

Reading Files with StreamReader

For reading text files, the StreamReader class provides useful high-level functionality. To use it, wrap a FileStream in a StreamReader and call ReadLine() and other methods to access file contents.

Here is an example:

using (StreamReader reader = new StreamReader("data.txt")) 
{
  string line;
  while ((line = reader.ReadLine()) != null) 
  {
    Console.WriteLine(line); 
  }
}

This loops through each line in the text file and prints it out. The using block ensures the reader is properly disposed.

Writing Files with StreamWriter

Similarly, for writing text files, use the StreamWriter class. You can wrap a FileStream in it or construct it directly with a file path.

Here‘s an example:

using (StreamWriter writer = new StreamWriter("log.txt"))
{
  writer.WriteLine("Activity log");  
  writer.WriteLine(DateTime.Now); 
}

This writes a couple lines of text to the file. Like StreamReader, it handles text encodings and provides helpful methods like WriteLine().

Appending to Existing Files

To add data to the end of an existing file, open the stream with FileMode.Append:

FileStream stream = new FileStream("log.txt", FileMode.Append);

Then call Write() methods as usual to append data.

Reading and Writing Binary Data

For binary data (bytes rather than text), use FileStream‘s ReadByte() and WriteByte() methods:

byte[] bytes = new byte[100];
int bytesRead = stream.Read(bytes, 0, 100); 

stream.Write(bytes, 0, bytesRead);

This reads binary data from a stream into a byte buffer and writes it back out again.

Random File Access with Seek()

By default, file streams open at the beginning of a file. But you can use Seek() to move the stream position, allowing random access at any location:

stream.Seek(50, SeekOrigin.Begin); // Seek to byte 50
byte b = stream.ReadByte(); // Read single byte from offset 50

SeekOrigin specifies whether the offset should be from the beginning, current position, or end of file.

Getting File Info

You can retrieve metadata on a file like its size using static File class methods:

long size = new FileInfo("data.bin").Length; 
DateTime time = File.GetLastWriteTime("data.bin");

Useful for getting file stats before reading or determining file availability.

Reading Large Files in Chunks

When accessing very large files, you generally want to avoid reading the entire contents into memory at once. Here is an example using a buffer and loop for reading in chunks:

const int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];  

int bytesRead;
using (FileStream stream = File.Open("massive-file.data")) 
{
  do
  {
    bytesRead = stream.Read(buffer, 0, bufferSize);
    // Process buffer contents

  } while (bytesRead > 0); 
}

This prevents out-of-memory crashes by only allocating a small fixed buffer. The data can then be processed in chunks as it is read.

File and Directory Operations

The static File and Directory classes provide a variety of utility methods for creating, deleting, moving, and querying files and directories.

For example, to copy a file:

File.Copy("source.txt", "destination.txt");

Or create a directory if it doesn‘t exist:

if (!Directory.Exists("logs"))
{
  Directory.CreateDirectory("logs");
}

You can also delete files, retrieve file lists in a directory, combine paths, get disk space info, and much more.

Asynchronous File I/O

All the file classes discussed also provide asynchronous versions of their methods to avoid blocking the calling thread:

await File.WriteAllBytesAsync(filePath, bytes);

Task<string> contentsTask = File.ReadAllTextAsync(filePath);

These asynchronous operations return Task objects. The await keyword (usable in async methods) pauses execution until the file task completes.

File and Directory Permissions

File system permissions can restrict or grant access when trying to open files from certain accounts or processes.

By default C# inherits the permissions of the account it is running under. But in some cases you may need elevated admin permissions or impersonation to access secured resources:

FileStream file = new FileStream(path, mode, access, share, bufferSize, options);

file.GetAccessControl();
File.GetAccessControl(path); 

File.SetAccessControl(path, security);

The key options and methods related to permissions are shown above. This allows getting or setting access control rules programmatically.

Conclusion

In this detailed guide we looked at many examples of opening, reading, writing, copying, appending to, and querying files in C# using classes like FileStream, StreamReader, StreamWriter and others.

Key concepts included:

  • Using FileStream for low-level byte/stream access
  • Leveraging StreamReader and StreamWriter for convenient text file handling
  • Appending data safely to existing files
  • Efficiently processing large files in chunks
  • Getting file metadata and manipulating the file system
  • Support for synchronous and asynchronous operations
  • Dealing with permissions restrictions

Together these I/O classes provide powerful and flexible file manipulation capabilities for C# applications. They are suitable for everyday text, binary, random access, and large dataset scenarios.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *