An INI File Parser in C#

Table of Contents

License

Introduction

Presentation

Syntax

Limitations

Usage

Revision History

2.00 (2013.11.13)

1.07 (2015.02.15)

1.06 (2013.10.26)

1.05 (2013.06.16)

1.04 (2013.05.23)

1.03 (2013.04.19)

1.02 (2013.04.17)

1.01 (2013.04.01)

1.00 (2013.03.31)

Alternatives

Comments and Complaints

License

The module has been donated to the Public Domain so that you may freely use it for anything you like.

Introduction

Many developers prefer the ancient Windows-style .ini files over the Windows registry. The reasons are obvious: The registry is usually the first thing that gets lots once a Windows installation breaks down, something which happens quite frequently, so the use of an external configuration file is often very attractive for experienced developers.

This page presents a very simple albeit rather powerful .NET implementation of a Windows-style .ini file parser.

Presentation

An .ini file is a very simple form of configuration file, yet it is suitable for most types of applications. Microsoft used to use .ini files until some no-brain at Microsoft decided to make life miserable for all users of all Microsoft products by inventing and deploying the ever-cursed Registry (a closed source binary database that virtually guarantees that you loose all your configuration settings whenever you eventually have to re-install your Windows box). Back in the old Windows 3.x days, you could simply make a copy of all your .ini files and then restore them once you had reinstalled your system - and you were set to play without spending days of reconfiguring your operating system and your applications endlessly.

Syntax

The syntax of a strict .ini file is very simple (LF means "linefeed" and SP means "space"):

File      = *Variable *Section .
Section   = Header *Variable .
Header    = "[" Name "]" *SP LF .
Name      = Word *(" " Word) .
Word      = +<any character but space and LF>.
Variable  = Name *SP "=" *SP +Character * SP LF .

Comments are not included in the grammar, but they basically begin with a semicolon and go to the end of the line.

Variables in the first, unnamed section are considered "global" variables. Variables inside sections are typically variables that only apply to a specific component of the application. For instance, the variable "color" in the section "Printer" might very well be a variable that configured printing to be either black-and-white or in full color.

Please notice that my initial implementation does not accept tabs in configuration files. The reason being that I loathe the odd interpretations of tabs that exist out there. Tabs should only be used in the beginning of the line and nowhere else. And in an .ini file, it does not make sense to begin a line with one or more tabs.

Limitations

The implementation is very simple and only offers the standard .NET way of testing if a field exists.

Usage

The module is straightforward to use:

try
{
    // The syntax specification is a basic list of "name = rule", where rule is a regular expression.
    string[] syntax =
    {
        @"Foo = ^(yes|no)$",
        @"",
        @"[Website]",
        @"Banner = ^..*$",
        @"Home = ^..*$",
        @"Statistics = ^(0|1)$"
    };

    // Load the .ini file as an array of lines.
    string[] lines = System.IO.File.ReadAllLines("program.ini");

    // Parse the .ini file according to our simple syntax specification.
    Dictionary<string, string> config = IniFile.Parse(lines, false, syntax);

    // Check if the 'Website' section is present.
    if (config.ContainsKey("Website"))
        System.Console.WriteLine("The Website section exists!");

    // Check if the 'Website.Home' key is present.
    if (config["Website"].ContainsKey("Home"))
        System.Console.WriteLine("The Website.Home value exists!");

    // Output a few values from our sample .ini file.
    System.Console.WriteLine("Foo = {0}", config["Foo"]);
    System.Console.WriteLine("Website.Banner = {0}", config["Website"]["Banner"]);
}
catch (IniFileError that)
{
    // Report an error to the console.
    System.Console.WriteLine("({0} {1}) Error: {2}", "program.ini", that.Line, that.Message);
}

Revision History

2.00 (2013.11.13)

  1. Renamed the class from Configuration to IniFile.
  2. Added support for an optional syntax specification, which is passed into the parser.
  3. Changed the class to be static so that no method has any persistent state whatsoever.
  4. Removed ToString() as the parser no longer takes a file name as its input, so only the client knows the name of the input file.

NOTICE:
v2.00 is highly incompatible with v1.x so you may want to download the latest v1.x if you just want to get going in a hurry.

Source code.

1.07 (2015.02.15)

  1. Fixed: The parser failen on valid files where there was a semi-colon (;) inside a quoted string.
  2. Fixed: The parser now only accepts comments that start in the first column.
  3. Fixed: Updated the comments to my current standard of a capitalized sentence followed by a dot.

Source code.

1.06 (2013.10.26)

  1. Fixed: The license is now Public Domain: You may do whatever you please with this code; no credits needed!

Source code.

1.05 (2013.06.16)

  1. Added: ConfigurationError.ToString() method for easily printing exception error messages.

Source code.

1.04 (2013.05.23)

  1. Fixed: John Sullivan kindly reported the problem that the parser did not handle name=data (no spaces!).

Source code.

1.03 (2013.04.19)

  1. Added: Configuration.ContainsKey() method to check if a given section is present or not.
  2. Added: Error message if the name part is missing from a given line (it starts with an equal sign (=)).

Source code.

1.02 (2013.04.17)

  1. Added: Support for quoted strings as right-hand-side values (name = "data").

Source code.

1.01 (2013.04.01)

  1. Fixed: The Configuration class failed to create sections prior to assigning values to them...

Source code.

1.00 (2013.03.31)

  1. Initial version.

Source code.

Alternatives

We have only been able to find one fairly sensible open source .ini file parser (which didn't surface easily in Google):

We cannot say if it is worth anything or not, but it is almost guaranteed way better than the sample code given here :-)

Comments and Complaints

Please feel free to write us if you find errors or find that it does not properly cover your needs. We won't promise to spend a lot of time on this trivial piece of code, but obviously we'll be happy to fix errors, and perhaps add this or that feature.

Please notice there are absolutely no plans to add write support to the module; we don't believe that apps should be meddling with the user's configuration files: If the user wants something changed in his or her configuration, the user should do it. Besides, the task of saving a configuration file is somewhat complicated by the presence of comments (one could parse and store them, though), and we don't need the ability to be able to modify and update configuration files from within our apps.

Please direct bug reports, comments, and enhancement requests to the mail address listed on this page.