C# > Data Access > File I/O > Reading and Writing Text Files

Reading and Writing Text to a File in C#

This code snippet demonstrates how to read and write text to a file using C#. It utilizes the `StreamWriter` and `StreamReader` classes for efficient file operations. The example covers creating a file, writing text to it, and then reading the text back from the file.

Basic File Writing Example

This code snippet creates a `StreamWriter` to write text to the file specified by `filePath`. The `using` statement ensures that the writer is properly disposed of, even if an exception occurs. The `WriteLine` method writes the string `textToWrite` to the file, adding a newline character at the end. A try-catch block is used to handle potential exceptions during file operations.

// Write text to a file
string filePath = "example.txt";
string textToWrite = "Hello, world! This is some text written to the file.";

try
{
    using (StreamWriter writer = new StreamWriter(filePath))
    {
        writer.WriteLine(textToWrite);
    }
    Console.WriteLine("Text written to file successfully.");
}
catch (Exception ex)
{
    Console.WriteLine($"An error occurred: {ex.Message}");
}

Basic File Reading Example

This code snippet creates a `StreamReader` to read text from the file specified by `filePath`. The `using` statement ensures proper disposal of the reader. The `ReadLine` method reads a line of text from the file. The read text is then printed to the console. A try-catch block handles potential exceptions during file operations.

// Read text from a file
string filePath = "example.txt";
string readText = "";

try
{
    using (StreamReader reader = new StreamReader(filePath))
    {
        readText = reader.ReadLine();
    }
    Console.WriteLine($"Text read from file: {readText}");
}
catch (Exception ex)
{
    Console.WriteLine($"An error occurred: {ex.Message}");
}

Complete Example: Writing and Reading

This example combines writing and reading text from a file. First, it attempts to write the `textToWrite` to the `example.txt` file using a `StreamWriter`. Then, it attempts to read the file line by line using a `StreamReader` and prints each line to the console. Error handling is included to catch potential exceptions during both write and read operations. The `while` loop ensures that all lines in the file are read until the end of the file is reached (`reader.ReadLine()` returns `null`).

using System;
using System.IO;

public class FileExample
{
    public static void Main(string[] args)
    {
        // File path
        string filePath = "example.txt";

        // Text to write
        string textToWrite = "This is a test line. This is another test line.";

        // Write to file
        try
        {
            using (StreamWriter writer = new StreamWriter(filePath))
            {
                writer.WriteLine(textToWrite);
                Console.WriteLine("Text written to file successfully.");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error writing to file: {ex.Message}");
            return;
        }

        // Read from file
        try
        {
            using (StreamReader reader = new StreamReader(filePath))
            {
                string line;
                while ((line = reader.ReadLine()) != null)
                {
                    Console.WriteLine($"Read line: {line}");
                }
                Console.WriteLine("Text read from file successfully.");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error reading from file: {ex.Message}");
        }
    }
}

Concepts Behind the Snippet

This snippet uses fundamental C# classes from the `System.IO` namespace for file operations. `StreamWriter` is used to write text to a file, while `StreamReader` is used to read text from a file. The `using` statement ensures that the file resources are properly closed and disposed of after use, even if exceptions occur, preventing resource leaks. Exception handling (try-catch blocks) is critical for robust file operations, allowing you to gracefully handle scenarios where files might not exist or access is denied.

Real-Life Use Case

Reading and writing text files is a common task in many applications. For example, you might use it to:

  • Store configuration settings for your application.
  • Log application events and errors.
  • Read data from a CSV file for processing.
  • Write data to a file for later analysis.
  • Create simple data stores for small applications.

Best Practices

  • Use `using` statements: Always wrap `StreamWriter` and `StreamReader` in `using` statements to ensure proper disposal of resources.
  • Handle Exceptions: Use `try-catch` blocks to handle potential `IOException` exceptions, such as file not found or access denied.
  • Specify Encoding: Consider specifying the text encoding when creating `StreamWriter` and `StreamReader` objects, especially when dealing with non-ASCII characters. UTF-8 is a common and recommended choice. Example: `new StreamWriter(filePath, false, Encoding.UTF8)`
  • File Paths: Use relative or absolute paths appropriately, considering the application's deployment location. The `Path.Combine` method can be used to reliably construct file paths.
  • Asynchronous Operations: For larger files or applications where performance is critical, consider using the asynchronous versions of `StreamWriter` and `StreamReader` methods (e.g., `WriteLineAsync`, `ReadLineAsync`).

Interview Tip

Be prepared to discuss the importance of resource management when working with files. Explain the purpose of the `using` statement and the potential consequences of not properly disposing of file streams. Also, be ready to discuss common exceptions that can occur during file operations and how to handle them.

When to Use Them

Use these techniques when you need to persist data to disk in a simple, human-readable format. Text files are suitable for configuration files, log files, or storing small amounts of structured data. For larger or more complex datasets, consider using databases or binary file formats.

Memory Footprint

The memory footprint depends on the size of the file being read or written. When writing, the entire string to be written needs to be in memory. When reading, the `StreamReader` typically buffers a portion of the file in memory. For very large files, consider reading or writing in smaller chunks to reduce memory consumption. Asynchronous operations can also help improve responsiveness when dealing with large files.

Alternatives

Alternatives to using `StreamWriter` and `StreamReader` include:

  • `File.WriteAllText` and `File.ReadAllText`: These static methods provide a simpler way to write and read entire files at once, but are not suitable for very large files.
  • BinaryReader and BinaryWriter: Used for reading and writing binary data to files.
  • XML Serialization: Used for writing and reading objects to and from XML files.
  • JSON Serialization: Used for writing and reading objects to and from JSON files.
  • Database Access (e.g., using ADO.NET or Entity Framework): Used for storing and retrieving data from relational databases.

Pros

  • Simple and Easy to Use: The `StreamWriter` and `StreamReader` classes are relatively straightforward to use.
  • Human-Readable Format: Text files are easy to read and edit manually.
  • Platform Independent: Text files can be easily transferred between different operating systems.

Cons

  • Performance: Reading and writing large text files can be slower compared to binary file formats.
  • Data Type Limitations: Text files only store text, so you need to convert data types to and from strings.
  • Parsing Complexity: Parsing structured data from text files can be more complex than using dedicated serialization formats like XML or JSON.

FAQ

  • How do I append text to an existing file?

    To append text to an existing file, create a `StreamWriter` with the `append` parameter set to `true`: `using (StreamWriter writer = new StreamWriter(filePath, true)) { ... }`
  • How do I specify the character encoding when reading or writing a file?

    You can specify the encoding in the `StreamWriter` and `StreamReader` constructors. For example, to use UTF-8 encoding: `new StreamWriter(filePath, false, Encoding.UTF8)` and `new StreamReader(filePath, Encoding.UTF8)`
  • What happens if the file does not exist when I try to read it?

    A `FileNotFoundException` will be thrown. You should handle this exception in a `try-catch` block.