Jump to content

Reading INI Files in Managed Code

- - - - -

  • Please log in to reply
1 reply to this topic

#1
sam_coder

sam_coder

    Programming Expert

  • Members
  • PipPipPipPipPipPip
  • 372 posts
Hey Guys,
I read a tutorial here the other day, INI Support with p/invokes. Very cool! I thought I would post a method I've used in the past, which is 100% managed, which can be really useful, especially if you target a non Microsoft CLR, like Mono..

So, here's a utility class:


using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.IO;


namespace utility {


    /// <summary>

    /// Read-Only Support for INI Configuration Files

    /// </summary>

    public static class INIUtility : object {


        #region [Local Attributes]


        /// <summary>

        /// Line Comment Tokens

        /// </summary>

        private static string[] m_strLineComments = new string[] { "#", ";" };


        #endregion


        #region [Popular Interface]


        /// <summary>

        /// Read a single Value from an INI Configuration File

        /// </summary>

        /// <param name="strFileSource">File Location</param>

        /// <param name="strSection">Section Name</param>

        /// <param name="strKey">Key Name</param>

        /// <returns>Value of setting</returns>

        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];

            else

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


        }


        /// <summary>

        /// Read an entire section

        /// </summary>

        /// <param name="strFileSource">File Location</param>

        /// <param name="strSection">Section Name</param>

        /// <returns>Dictionary, Containing Section Keys/Values</returns>

        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 + "]");

            if (intSectionPos == -1)

                throw new Exception("Section strFileSource Found");


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

            foreach (string strKeyPair in strKeyPairs) {

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

                if (strKeyValuePair.Length > 0)

                    if (strKeyValuePair.Length == 2)

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

                    else

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

            }


            return dictRet;

        }


        /// <summary>

        /// Read Entire INI Configuration File

        /// </summary>

        /// <param name="strFileSource">File to Read</param>

        /// <returns>Collection containing all sections. and key/value pairs for each section</returns>

        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;

        }


        #endregion


        #region [Helper Methods]


        /// <summary>

        /// Read the entire contents of the specified file (minus the comments)

        /// </summary>

        /// <param name="strFilePath">Path of file</param>

        /// <returns>Entire file contents in a string</returns>

        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

        }


        /// <summary>

        /// Strips comments from INI Source

        /// </summary>

        /// <param name="strSource">Actual INI Configuration Contents</param>

        /// <returns>INI Configuration Contents (minus comments)</returns>

        private static string CleanComments(string strSource) {

            StringBuilder sbResponse = new StringBuilder();

            string[] strBuffer;

            //Propigate Line

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


            //Process Each Item in array

            foreach (string strLine in strLines) {

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

                    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();

        }


        /// <summary>

        /// Pulls INI Section (Header and Key/Value Pairs)

        /// </summary>

        /// <param name="strSource">Source Configuration Contents</param>

        /// <param name="strSection">Name of section to pull</param>

        /// <returns>Entire Section, Header and Key/Value Pairs</returns>

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


            //Grab index of the section

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

            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);

            if (intEndSectionPos == -1)

                intEndSectionPos = strSource.Length;

            else

                intEndSectionPos -= 1;


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

        }


        /// <summary>

        /// Return the names of all sections in an INI configuration

        /// </summary>

        /// <param name="strSource">INI Configuration Contents</param>

        /// <returns>String array containing all section names</returns>

        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);

            while (intLastPos != -1) {

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


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


                intLastPos += 1;

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

            }


            return lstSections.ToArray();

        }


        #endregion


    }

}


This allows you to read individual settings, groups or entire files. I've exposed groups and files with the dictionary object, which is really neat. Here's how it works. Take an INI file that looks like this.


#settings

[general]

key1=value1

key2=value2


[special]

special_key=special_value



You could retrieve an individual setting like this:


string setting_value = INIUtility.Read(@"/path/to/conf.ini", "general", "key2");

Console.WriteLine(setting_value);


Or you could read a group like this:


Dictionary<string, string> section = INIUtility.ReadAllSection(@"/path/to/conf.ini", "general");

Console.WriteLine(section["key1"]);

Console.WriteLine(section["key2"]);


Or you could read the whole file:

Dictionary<string, Dictionary<string, string>> settings = INIUtility.ReadAll(@"/path/to/conf.ini");

Console.WriteLine(settings["general"]["key1"]);

Console.WriteLine(settings["general"]["key2"]);

Console.WriteLine(settings["special"]["special_key"]);


I chose to do this read only, in this way, because it exposes the settings in a really intuitive way. That and I rarely ever even want to write settings to an INI file, so it keeps the code, nice and clean, and easy to follow.

Enjoy!

Edited by sam_coder, 10 December 2010 - 10:16 AM.
noticed a bug in the code


#2
delraich

delraich

    Newbie

  • Members
  • Pip
  • 5 posts
thanks




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users