C sharp:ConfigurationFileClass

From GPWiki
Jump to: navigation, search

Note: The correct name of this page is C#:Configuration File Class; however, due to technical restrictions it is represented as C sharp:ConfigurationFileClass instead.

Introduction

The .NET Framework has built-in capability for using XML application settings files. These are a great way for making easily configurable applications with .NET, but if you are only loading a few variables this way and not using XML anywhere else then that means the System.Xml assembly (nearly 2 megabytes in size) is being loaded by your application just to load a few lines of configuration data. This overhead can significantly slow your application’s start up time. You can replicate the XML setting functionality just by writing a simple class. This will be much faster and use far less memory than using XML.

An example configuration file "config.txt" would look like this:

Configuration file modify at your own risk! Width = 800 Height = 600 Depth = 32 MipMap = True Fullscreen = True

The values are named and separated by line. This gives us a very simple human-readable format for getting simple configuration values.

Configuration Class

The class for reading the config file is as follows:

using System; using System.IO; using System.Collections.Generic; using System.Text.RegularExpressions;

/// <summary> /// Loads configuration data from a file. /// </summary> public class Configuration {

   //static fields
   private static Regex lineRegex = new Regex(@"([A-Z][A-Z0-9]*)[ \t]*=[ \t]*(.+)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
   
   //fields
   private Dictionary<string, string> values = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
   //constructor
   /// <summary>
   /// Loads configuration data.
   /// </summary>
   /// <param name="filepath">The path location of the configuration data.</param>
   public Configuration(string filepath)
   {
       using (FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read)) //Load a FileStream with the filepath string.
       {
           using (StreamReader sr = new StreamReader(fs))//use a StreamReader on the FileStream
           {
               while (!sr.EndOfStream) //loop through the entire file.
               {
                   string line = sr.ReadLine(); //read each line.
                   Match m = lineRegex.Match(line); //try to match the line with the regex.
                   if (m.Success) //test for successful match.
                   {
                       values.Add(m.Groups[1].Value, m.Groups[2].Value); //store the key/value pair in the dictionary.
                   }
               }
           }
       }
   }
   /// <summary>
   /// Returns a the value of a key from the configuration.
   /// </summary>
   /// <param name="key">The key for which to retrieve a value. If the key is not found null will be returned.</param>
   /// <returns>A string value associated with the key, or null if the key is not found.</returns>
   public string GetValue(string key)
   {
       string value;
       values.TryGetValue(key, out value); //Retrieve the value from the dictionary.
       return value;
   }

}

Note that the Regex will only match lines with a XXXX = YYYY pattern, where XXXX is the key and YYYY is value. Although, you can put tabs and spaces around the equals sign and it will still match. This means it did not match the warning at the top of the configuration file, and will not match other lines that do not follow the pattern. In addition, the dictionary is not case-sensitive, so it is easier to manually edit the file.

Usage

Using the class is very simple. This code loads a config file and reads a value:

Configuration config = new Configuration("config.txt"); string value = config.GetValue("Height"); int Height = int.Parse(value); //convert to the type we want to use

It only returns string so you will need to further convert the value to a type that you can use. If the value cannot be found then null is returned, so you should error check for this before trying to convert the string to another type (perhaps revert to a default value).

Conclusion

This code provides an extremely lightweight implementation of basic configuration-reading functionality. It does not have as many features as XML but in this scenario, those features are unnecessary. If the data becomes more hierarchical then XML is the way to go. XML is great for many things, but it is a lot of overhead for this simple task.