Reading INI Files in .Net

12/17/2008

I've been programming for a while, not as long as some out there, but for a while. I have programs/code that I've written that has spanned from QBasic to C to Java to, well, you name it really. With each language you gain some things and lose some things. And if you switch platforms, you're definitely getting different libraries, etc... Now that I'm primarily on C#, I have to deal with the lose of a decent built in INI file reader/writer. In C++ (assuming you're on windows), you just call GetPrivateProfileString and I was good to go. In .Net, there's nothing.

It makes perfect sense that there wouldn't be anything in .Net for reading from or writing to an INI file:

  1. They want you to use XML (which you should be doing)
  2. Use XML already!

But you may run into situations where you need to read an INI file to move over settings. In these instances you're going to have to read it in yourself... Or use this class that I've created... The choice is yours really:

   1: /*
   2: Copyright (c) 2010 <a href="http://www.gutgames.com">James Craig</a>
   3: 
   4: Permission is hereby granted, free of charge, to any person obtaining a copy
   5: of this software and associated documentation files (the "Software"), to deal
   6: in the Software without restriction, including without limitation the rights
   7: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8: copies of the Software, and to permit persons to whom the Software is
   9: furnished to do so, subject to the following conditions:
  10: 
  11: The above copyright notice and this permission notice shall be included in
  12: all copies or substantial portions of the Software.
  13: 
  14: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20: THE SOFTWARE.*/
  21:  
  22: #region Usings
  23: using System;
  24: using System.Collections.Generic;
  25: using System.Linq;
  26: using System.Text;
  27: using System.Text.RegularExpressions;
  28: using Utilities.IO;
  29:  
  30: #endregion
  31:  
  32: namespace Utilities.FileFormats.INI
  33: {
  34:     /// <summary>
  35:     /// Class for helping with INI files
  36:     /// </summary>
  37:     public class INI
  38:     {
  39:         #region Constructor
  40:         /// <summary>
  41:         /// Constructor
  42:         /// </summary>
  43:         public INI()
  44:         {
  45:             LoadFile();
  46:         }
  47:  
  48:         /// <summary>
  49:         /// Constructor
  50:         /// </summary>
  51:         /// <param name="FileName">Name of the file</param>
  52:         public INI(string FileName)
  53:         {
  54:             this.FileName = FileName;
  55:             LoadFile();
  56:         }
  57:         #endregion
  58:  
  59:         #region Public Functions
  60:         /// <summary>
  61:         /// Writes a change to an INI file
  62:         /// </summary>
  63:         /// <param name="Section">Section</param>
  64:         /// <param name="Key">Key</param>
  65:         /// <param name="Value">Value</param>
  66:         public void WriteToINI(string Section, string Key, string Value)
  67:         {
  68:             if (_FileContents.Keys.Contains(Section))
  69:             {
  70:                 if (_FileContents[Section].Keys.Contains(Key))
  71:                 {
  72:                     _FileContents[Section][Key] = Value;
  73:                 }
  74:                 else
  75:                 {
  76:                     _FileContents[Section].Add(Key, Value);
  77:                 }
  78:             }
  79:             else
  80:             {
  81:                 Dictionary<string, string> TempDictionary = new Dictionary<string, string>();
  82:                 TempDictionary.Add(Key, Value);
  83:                 _FileContents.Add(Section, TempDictionary);
  84:             }
  85:             WriteFile();
  86:         }
  87:  
  88:         /// <summary>
  89:         /// Reads a value from an INI file
  90:         /// </summary>
  91:         /// <param name="Section">Section</param>
  92:         /// <param name="Key">Key</param>
  93:         /// <param name="DefaultValue">Default value if it does not exist</param>
  94:         public string ReadFromINI(string Section, string Key, string DefaultValue)
  95:         {
  96:             if (_FileContents.Keys.Contains(Section))
  97:             {
  98:                 if (_FileContents[Section].Keys.Contains(Key))
  99:                 {
 100:                     return _FileContents[Section][Key];
 101:                 }
 102:             }
 103:             return DefaultValue;
 104:         }
 105:  
 106:         /// <summary>
 107:         /// Returns an XML representation of the INI file
 108:         /// </summary>
 109:         /// <returns>An XML representation of the INI file</returns>
 110:         public string ToXML()
 111:         {
 112:             if (string.IsNullOrEmpty(this.FileName))
 113:                 return "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<INI>\r\n</INI>";
 114:             StringBuilder Builder = new StringBuilder();
 115:             Builder.Append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n");
 116:             Builder.Append("<INI>\r\n");
 117:             foreach (string Header in _FileContents.Keys)
 118:             {
 119:                 Builder.Append("<section name=\"" + Header + "\">\r\n");
 120:                 foreach (string Key in _FileContents[Header].Keys)
 121:                 {
 122:                     Builder.Append("<key name=\"" + Key + "\">" + _FileContents[Header][Key] + "</key>\r\n");
 123:                 }
 124:                 Builder.Append("</section>\r\n");
 125:             }
 126:             Builder.Append("</INI>");
 127:             return Builder.ToString();
 128:         }
 129:         #endregion
 130:  
 131:         #region Private Functions
 132:         /// <summary>
 133:         /// Writes the INI information to a file
 134:         /// </summary>
 135:         private void WriteFile()
 136:         {
 137:             if (string.IsNullOrEmpty(this.FileName))
 138:                 return;
 139:             StringBuilder Builder = new StringBuilder();
 140:             foreach (string Header in _FileContents.Keys)
 141:             {
 142:                 Builder.Append("[" + Header + "]\r\n");
 143:                 foreach (string Key in _FileContents[Header].Keys)
 144:                 {
 145:                     Builder.Append(Key + "=" + _FileContents[Header][Key] + "\r\n");
 146:                 }
 147:             }
 148:             FileManager.SaveFile(Builder.ToString(), FileName);
 149:         }
 150:  
 151:         /// <summary>
 152:         /// Loads an INI file
 153:         /// </summary>
 154:         private void LoadFile()
 155:         {
 156:             _FileContents = new Dictionary<string, Dictionary<string, string>>();
 157:             if (string.IsNullOrEmpty(this.FileName))
 158:                 return;
 159:  
 160:             string Contents = FileManager.GetFileContents(FileName);
 161:             Regex Section = new Regex("[" + Regex.Escape(" ") + "\t]*" + Regex.Escape("[") + ".*" + Regex.Escape("]\r\n"));
 162:             string[] Sections = Section.Split(Contents);
 163:             MatchCollection SectionHeaders = Section.Matches(Contents);
 164:             int Counter = 1;
 165:             foreach (Match SectionHeader in SectionHeaders)
 166:             {
 167:                 string[] Splitter = { "\r\n" };
 168:                 string[] Splitter2 = { "=" };
 169:                 string[] Items = Sections[Counter].Split(Splitter, StringSplitOptions.RemoveEmptyEntries);
 170:                 Dictionary<string, string> SectionValues = new Dictionary<string, string>();
 171:                 foreach (string Item in Items)
 172:                 {
 173:                     SectionValues.Add(Item.Split(Splitter2, StringSplitOptions.None)[0], Item.Split(Splitter2, StringSplitOptions.None)[1]);
 174:                 }
 175:                 _FileContents.Add(SectionHeader.Value.Replace("[", "").Replace("]\r\n", ""), SectionValues);
 176:                 ++Counter;
 177:             }
 178:         }
 179:  
 180:         #endregion
 181:  
 182:         #region Properties
 183:         /// <summary>
 184:         /// Name of the file
 185:         /// </summary>
 186:         public string FileName
 187:         {
 188:             get { return _FileName; }
 189:             set { _FileName = value; LoadFile(); }
 190:         }
 191:  
 192:         private Dictionary<string, Dictionary<string, string>> FileContents
 193:         {
 194:             get { return _FileContents; }
 195:             set { _FileContents = value; }
 196:         }
 197:         private string _FileName = string.Empty;
 198:         private Dictionary<string, Dictionary<string, string>> _FileContents = null;
 199:         #endregion
 200:     }
 201: }

I've commented the class. All it really does is parses the file, finding each section, key, and value and holds it in a Dictionary object (which contains the section header and another dictionary containing the key/value pairs). It even writes the file out if you need or puts it into an XML format if you'd like to parse that instead. Anyway, I hope this helps out someone. So give it a try, leave feedback, and happy coding.



Comments