Click here to Skip to main content
15,879,535 members
Articles / Programming Languages / C#
Tip/Trick

IniReader: A Simple, Tiny INI Reader

Rate me:
Please Sign up or sign in to vote.
5.00/5 (17 votes)
13 Dec 2021MIT2 min read 11.3K   343   29   11
An INI reader that is easy to use, easy to port, relatively efficient, and to the point
This is a simple single file INI reader drop in for C# projects.

Introduction

Occasionally, I need to read INI files, usually as one small part of a much larger project. As such, I really don't want it to require a bunch of infrastructure or other "buy in". I just want something simple that will get me what I need without much fuss. There is a lot of INI reader code out there, but a lot of it is gold plated, and so it wasn't what I needed. This is something simple and to the point you can just copy and paste into your project.

Using the Code

INI files are often laid out in sections, and under each section, there are key value pairs. This INI reader will combine multiple sections with the same name, and multiple keys under the same section as a single key with multiple values. It also allows you to specify values for a key one for each line. The default section is the empty string.

There are two methods of interest. The first one is for reading an INI file:

C#
var ini = Ini.Read(myTextReader);

The caller is responsible for closing the reader. The ini variable now contains a type of IDictionary<string, IDictionary<string, object>>. That's kind of a hairy type but using it is pretty simple.

Each [section] in the INI file is a key in the outer dictionary. Each key = value pair becomes a dictionary entry under that section entry's inner dictionary. If there is only one value for a key, the dictionary value will be a string. If there is more than one value per key, that key's value will be an IList<string>.

As far as the other method, you can dump the results to a string using Ini.ToString(ini).

If you want to just paste it from here, here is the relevant code in its entirety:

C#
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

static class Ini {
    static void AddIniEntryValue(IDictionary<string, object> d, string line, 
                                 string name, int i, object o) {
        if (o is string) {
            var s = (string)o;
            d.Remove(name);
            var col = new List<string>();
            d.Add(name, col);
            col.Add(s);
            col.Add(line.Substring(i + 1).TrimStart());
        } else {
            ((List<string>)o).Add(line.Substring(i + 1).TrimStart());
        }
    }
    /// <summary>
    /// Reads an INI file as a nested dictionary. 
    /// The outer dictionary contains a dictionary for each section. 
    /// The inner dictionary contains a name, and a string or a list of strings 
    /// when an entry has multiple items.
    /// </summary>
    /// <param name="reader">The text reader</param>
    /// <param name="comparer">The comparer to use for keys. 
    /// Defaults to culture invariant and case insensitive.</param>
    /// <returns>A nested dictionary</returns>
    public static IDictionary<string, IDictionary<string, object>> 
           Read(TextReader reader, IEqualityComparer<string> comparer = null) {
        if (comparer == null) {
            comparer = StringComparer.InvariantCultureIgnoreCase;
        }
        int lc = 1;
        var result = new Dictionary<string, IDictionary<string, object>>(comparer);
        string section = "";
        string name = null;
        string line;
        while (null != (line = reader.ReadLine())) {
            var i = line.IndexOf(';');
            if (i > -1) {
                line = line.Substring(0, i);
            }
            line = line.Trim();
            if (!string.IsNullOrEmpty(line)) {
                IDictionary<string, object> d;
                if (line.Length > 2 && line[0] == '[' && line[line.Length - 1] == ']') {
                    section = line.Substring(1, line.Length - 2);
                    if (!result.TryGetValue(section, out d)) {
                        d = new Dictionary<string, object>(comparer);
                        result.Add(section, d);
                    }
                } else {
                    d = result[section];
                    i = line.IndexOf('=');
                    if (i > -1) {
                        name = line.Substring(0, i).TrimEnd();
                        object o;
                        if (d.TryGetValue(name, out o)) {
                            AddIniEntryValue(d, line, name, i, o);
                        } else
                            d.Add(name, line.Substring(i + 1).TrimStart());
                    } else if (null == name) {
                        throw new IOException("Invalid INI file at line " + lc.ToString());
                    } else {
                        var o = d[name];
                        AddIniEntryValue(d, line, name, i, o);
                    }
                }
            }
            ++lc;
        }
        return result;
    }
    public static string ToString(IDictionary<string,IDictionary<string,object>> ini) {
        var sb = new StringBuilder();
        foreach (var sentry in ini) {
            sb.AppendLine("[" + sentry.Key + "]");
            var d = sentry.Value;
            foreach (var entry in d) {
                if (entry.Value is IList<string>) {
                    var l = ((IList<string>)entry.Value);
                    sb.AppendLine(string.Format("{0} = {1}", entry.Key, l[0]));
                    for (var i = 1; i < l.Count; ++i) {
                        sb.AppendLine("\t" + l[i]);
                    }
                    sb.AppendLine();
                } else
                    sb.AppendLine(string.Format("{0} = {1}", entry.Key, entry.Value));
            }
        }
        return sb.ToString();
    }
}

History

  • 13th December, 2021 - Initial submission
  • 13th December, 2021 - Bugfix

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
United States United States
Just a shiny lil monster. Casts spells in C++. Mostly harmless.

Comments and Discussions

 
QuestionThank You! Pin
Angelo Cresta29-Dec-21 3:55
professionalAngelo Cresta29-Dec-21 3:55 
QuestionThank you Pin
Bobby.Dean14-Dec-21 11:32
Bobby.Dean14-Dec-21 11:32 
GeneralI did the same thing years ago. Pin
Bruce Greene14-Dec-21 8:49
Bruce Greene14-Dec-21 8:49 
GeneralRe: I did the same thing years ago. Pin
Lutz Hamann20-Dec-21 3:35
Lutz Hamann20-Dec-21 3:35 
GeneralMy vote of 5 Pin
Mark Miller14-Dec-21 7:02
Mark Miller14-Dec-21 7:02 
GeneralMy vote of 5 Pin
Member 1370414314-Dec-21 1:51
Member 1370414314-Dec-21 1:51 
Very good as usual. Thank you
Hoping for the next step: ini writer!
GeneralRe: My vote of 5 Pin
honey the codewitch14-Dec-21 4:47
mvahoney the codewitch14-Dec-21 4:47 
GeneralMy vote of 5 Pin
Klaus Luedenscheidt13-Dec-21 17:52
Klaus Luedenscheidt13-Dec-21 17:52 
GeneralMy vote of 5 Pin
Foglio7713-Dec-21 10:30
Foglio7713-Dec-21 10:30 
GeneralRe: My vote of 5 Pin
honey the codewitch13-Dec-21 10:41
mvahoney the codewitch13-Dec-21 10:41 
GeneralRe: My vote of 5 Pin
honey the codewitch13-Dec-21 10:47
mvahoney the codewitch13-Dec-21 10:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.