Try it yourself with our free Env Diff tool — runs entirely in your browser, no signup needed.

How to Parse .env files in C#

How to Parse .env Files in C#

Parsing .env files is a crucial step in many C# applications, especially those using the 12-factor app methodology. These files store sensitive configuration data, such as database credentials and API keys, in a simple key-value format. In this article, we'll explore how to parse .env files in C# efficiently and effectively.

Quick Example

Here's a minimal example that demonstrates how to parse a .env file using the System.IO and System.Linq namespaces:

using System;
using System.IO;
using System.Linq;

class EnvParser
{
    public static Dictionary<string, string> ParseEnvFile(string filePath)
    {
        return File.ReadAllLines(filePath)
            .Select(line => line.Split(new[] { '=' }, 2))
            .Where(parts => parts.Length == 2)
            .ToDictionary(parts => parts[0].Trim(), parts => parts[1].Trim());
    }
}

// Usage
var envVars = EnvParser.ParseEnvFile(".env");
Console.WriteLine(envVars["DB_HOST"]); // Output: "localhost"

This code reads the .env file, splits each line into key-value pairs, and returns a dictionary of environment variables.

Step-by-Step Breakdown

Let's dissect the code:

  1. File.ReadAllLines(filePath): Reads the entire file into an array of strings, where each string represents a line in the file.
  2. Select(line => line.Split(new[] { '=' }, 2)): Splits each line into an array of two strings using the = character as the separator. The 2 parameter limits the split to two parts, ensuring we don't split values that contain = characters.
  3. Where(parts => parts.Length == 2): Filters out any lines that don't contain exactly two parts (i.e., invalid lines).
  4. ToDictionary(parts => parts[0].Trim(), parts => parts[1].Trim()): Converts the array of key-value pairs into a dictionary, trimming any whitespace from the keys and values.

Handling Edge Cases

Empty/Null Input

If the input file is empty or null, the File.ReadAllLines method will return an empty array. We can handle this by adding a simple null check:

if (string.IsNullOrEmpty(filePath))
{
    return new Dictionary<string, string>();
}

Invalid Input

If the input file contains invalid lines (e.g., lines without an = character), the Where clause will filter them out. However, if you want to handle such cases explicitly, you can add a custom validation step:

.Where(parts => parts.Length == 2 && parts[0].Trim() != string.Empty)

Large Input

For very large .env files, reading the entire file into memory might be inefficient. In such cases, you can use a streaming approach using File.ReadLines instead:

foreach (var line in File.ReadLines(filePath))
{
    // Process each line individually
}

Unicode/Special Characters

The Split method used in the example handles Unicode characters correctly. However, if you need to support special characters in your .env file (e.g., newline characters), you may need to use a more sophisticated parsing approach, such as using a regular expression.

Common Mistakes

1. Forgetting to Trim Whitespace

Incorrect code:

ToDictionary(parts => parts[0], parts => parts[1])

Corrected code:

ToDictionary(parts => parts[0].Trim(), parts => parts[1].Trim())

2. Not Handling Empty/Null Input

Incorrect code:

File.ReadAllLines(filePath); // Throws exception if filePath is null

Corrected code:

if (string.IsNullOrEmpty(filePath))
{
    return new Dictionary<string, string>();
}
File.ReadAllLines(filePath);

3. Not Validating Input

Incorrect code:

.Select(line => line.Split(new[] { '=' }, 2))
    .ToDictionary(parts => parts[0], parts => parts[1])

Corrected code:

.Select(line => line.Split(new[] { '=' }, 2))
    .Where(parts => parts.Length == 2 && parts[0].Trim() != string.Empty)
    .ToDictionary(parts => parts[0].Trim(), parts => parts[1].Trim())

Performance Tips

  1. Use File.ReadLines instead of File.ReadAllLines for large files: This approach allows you to process the file line-by-line, reducing memory usage.
  2. Use a StringBuilder for concatenating strings: When building the dictionary, use a StringBuilder to concatenate the key-value pairs instead of using the + operator.
  3. Avoid using Regex for simple parsing: While regular expressions can be powerful, they can also be slower than simple string manipulation methods.

FAQ

Q: How do I handle commented lines in my .env file?

A: You can add a simple check to ignore lines that start with a # character:

.Where(line => !line.StartsWith("#"))

Q: Can I use this approach for parsing other types of files?

A: While this approach is specific to .env files, you can adapt it to parse other types of files with similar formats.

Q: How do I handle environment variables with multiple values?

A: You can modify the parsing logic to handle arrays of values instead of single values.

Q: Can I use this code in a production environment?

A: Yes, this code is suitable for production use, but be sure to add proper error handling and logging mechanisms.

Q: How do I install the required dependencies?

A: You don't need to install any external dependencies for this code, as it only uses built-in .NET Framework classes.

AI agent tools available. The CodeTidy MCP Server gives Claude, Cursor, and other AI agents access to 60+ developer tools. One command: npx @codetidy/mcp