Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
- - - - -

INI Parsing

configuration .net ini c#

  • Please log in to reply
No replies to this topic

#1 sam_coder

sam_coder

    CC Addict

  • Senior Member
  • PipPipPipPipPip
  • 380 posts

Posted 13 May 2014 - 07:57 AM

So, I've been quiet for so long.  Just casually lurking here.  I know I never really completed my ADO tutorials, so hard to find the time...  Anywho, I thought I would make another contribution!

 

I've always felt that, the fact that .NET doesn't provide support for INI out of the box; somewhat lacking.  A while back I had a need for this, and ended up writing my own parser.  It's pretty simple, and I'm not sure if there are any standards or level of compliance to consider it legit; though I thought it worthwhile sharing.  I use it often enough.

 

For those unaware, INI configuration files used to be insanely popular.  In fact, even with the existence of amazing XML processing libraries, it's hard to beat the simplicity of INI.  For those situations where you have really basic offline configuration needs.

 

So, the makeup of these configuration files is simple.  They are plain text, are made up sections or groups, and each section or group can have as many key value pairs as you want.  The keys and values are considered strings, and the configuration files can be marked up with comments.

 

An example:

#comment
[Section]
key=value
key=value
 
#comment
[section]
key=value
key=value

As soon as I started thinking about how to handle these things, I thought of the Generic Dictionary.  Dictionary<string, string> actually conceptually works very well with this!  And Dictionary<string, Dictionary<string, string>> could cover the whole file.

 

So thats basically my approach.  Simple read a file, line by line, and using simple string split operations to break down comments, section/group headers and key/value pairs.

 
private static readonly string[] m_strLineComments = new string[] { "#", ";" };
 
public static string Read(string strFileSource, string strSection, string strKey) {

    Dictionary<string, string> dictKeys = ReadAllSection(strFileSource, strSection);



    if (dictKeys.Keys.Contains(strKey))

        return dictKeys[strKey];

    throw new Exception("Key does not exist");

}
 
public static Dictionary<string, string> ReadAllSection(string strFileSource, string strSection) {

    Dictionary<string, string> dictRet = new Dictionary<string, string>();



    //Grab Contents of section

    string strContents = GetSection(ReadFile(strFileSource), strSection);

    string[] strKeyPairs, strKeyValuePair;



    //Grab index of the section

    int intSectionPos = strContents.IndexOf("[" + strSection + "]", StringComparison.InvariantCulture);

    if (intSectionPos == -1)

        throw new Exception("Section strFileSource Found");



    strKeyPairs = strContents.Substring(("[" + strSection + "]").Length).Split(new[] { "\r", "\n" }, StringSplitOptions.None);

    foreach (string strKeyPair in strKeyPairs) {

        strKeyValuePair = strKeyPair.Split(new[] { "=" }, StringSplitOptions.RemoveEmptyEntries);

        if (strKeyValuePair.Length > 0)

            if (strKeyValuePair.Length == 2)

                dictRet[strKeyValuePair[0]] = strKeyValuePair[1];

            else

                dictRet[strKeyValuePair[0]] = string.Empty;

    }



    return dictRet;

}
 
public static Dictionary<string, Dictionary<string, string>> ReadAll(string strFileSource) {

    Dictionary<string, Dictionary<string, string>> dictValues = new Dictionary<string, Dictionary<string, string>>();

    Dictionary<string, string> dictKeyPairs;

    string strContents = ReadFile(strFileSource);



    foreach (string strSection in GetSections(strContents)) {

        dictValues[strSection] = new Dictionary<string, string>();

        dictKeyPairs = ReadAllSection(strFileSource, strSection);

        foreach (string strKey in dictKeyPairs.Keys) {

            dictValues[strSection][strKey] = dictKeyPairs[strKey];

        }

    }



    return dictValues;

}
 
private static string ReadFile(string strFilePath) {

    StringBuilder sbInput = new StringBuilder();



    using (StreamReader sr = new StreamReader(strFilePath)) {

        sbInput.Append(sr.ReadToEnd());

    }

    return CleanComments(sbInput.ToString()); //And clean the comments

}
 
private static string CleanComments(string strSource) {

    StringBuilder sbResponse = new StringBuilder();

    string[] strBuffer;

    //Propigate Line

    string[] strLines = strSource.Split(new[] { "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);



    //Process Each Item in array

    foreach (string strLine in strLines) {

        if (!m_strLineComments.Contains(strLine.Trim()[0].ToString(CultureInfo.InvariantCulture))) {

            strBuffer = strLine.Split(m_strLineComments, StringSplitOptions.RemoveEmptyEntries);

            if (strBuffer.Length > 1) {

                sbResponse.Append(strBuffer[0]);

            } else {

                sbResponse.Append(strLine);

            }

            sbResponse.Append("\r\n");

        }

    }



    return sbResponse.ToString();

}
 
private static string GetSection(string strSource, string strSection) {



    //Grab index of the section

    int intSectionPos = strSource.IndexOf("[" + strSection + "]", StringComparison.InvariantCulture);

    if (intSectionPos == -1)

        throw new Exception("Section Not Found");



    //We know the end of the section, either by another [, or by the EOF

    int intEndSectionPos = strSource.IndexOf("[", intSectionPos + 1, StringComparison.InvariantCulture);

    if (intEndSectionPos == -1)

        intEndSectionPos = strSource.Length;

    else

        intEndSectionPos -= 1;



    return strSource.Substring(intSectionPos, intEndSectionPos - intSectionPos);

}
 
private static string[] GetSections(string strSource) {

    int intLastPos = 0;

    int intEndSectPos;

    List<string> lstSections = new List<string>();



    //Ok, the idea is to keep looking for indexes of section beginnings,

    intLastPos = strSource.IndexOf("[", intLastPos, StringComparison.InvariantCulture);

    while (intLastPos != -1) {

        intEndSectPos = strSource.IndexOf("]", intLastPos, StringComparison.InvariantCulture);



        lstSections.Add(strSource.Substring(intLastPos + 1, (intEndSectPos - 1) - (intLastPos)));



        intLastPos += 1;

        intLastPos = strSource.IndexOf("[", intLastPos, StringComparison.InvariantCulture);

    }



    return lstSections.ToArray();

}

So usage is pretty simple.  I'd tuck that into a new class called something like INI Parser, you'll need the following using directives.

using System;

using System.Collections.Generic;

using System.Globalization;

using System.Linq;

using System.Text;

using System.IO;

Right away, this doesn't look very memory effecient.  It's something I'm willing to live with, most of these files arent terribly big.  And I more or less designed this to pull the values to use, and then just forget about, or to read once and stick it somewhere so I can keep referencing it.  Where it shines is in how simple this exposes settings to you.

 

Take the following INI configuration file.  Lets say it's named test.ini, and it's located in your working directory.

###

### This is a sample INI Configuration File.

### All these settings are here simply to show you how this parser works

###

[User Settings]

Name=George

EmailAddress=something@somewhere.com



#another comment

[sectionB]

Setting=Value

Setting2=Some other Value

Storing the file in Dictionarys, is extreamly intuitive.

static void Main(string[] args) {

    Console.WriteLine("Some INI fun!");



    /**

     * Reading an entire file into memory

     * very flexible, though wont pick up changes to the file

     */



    Console.WriteLine("Reading INI Configuration File");

    var iniFile = INIParser.ReadAll("test.ini");



    Console.WriteLine("Enumerating all data from configuration file");

    foreach (var section in iniFile.Keys)

    {

        Console.WriteLine("Section: {0}", section);

        foreach (var setting in iniFile[section].Keys)

        {

            Console.WriteLine("Setting: {0} Value: {1}", setting, iniFile[section][setting]);

        }

        Console.WriteLine();

    }

    Console.WriteLine();



    /**

     * We can also read only a specific section of an INI file

     */



    Console.WriteLine("Pulling a section");

    var iniSection = INIParser.ReadAllSection("test.ini", "User Settings");

    Console.WriteLine("Section: {0}", "User Settings");

    foreach (var setting in iniSection.Keys) {

        Console.WriteLine("Setting: {0} Value: {1}", setting, iniSection[setting]);

    }

    Console.WriteLine();

    Console.WriteLine();



    /**

     * Or we can simply pull one value out of the file.

     */



    Console.WriteLine("Pulling a specific setting");

    Console.WriteLine(INIParser.Read("test.ini", "User Settings", "EmailAddress"));



    Console.WriteLine("Press <Enter> to terminate...");

    Console.ReadLine();

}

I'm not entirely sure I can call this a tutorial, though the code is pretty straight forward.  I certainly hope it helps at least one of you guys, or you learn somthing new!

 

Thanks!


  • 0





Also tagged with one or more of these keywords: configuration, .net, ini, c#