Now, don't get me wrong -- SQLite has its merits and XML is great if you want to store hierarchical data or if you need to configure your .NET application (which happens to already speak the lingo). But the reality is that INI serves quite well for a number of uses -- indeed, it can also be used to store hierarchical data, as you'd see if you checked out the innards of a .reg file. In particular, INI files are dead-easy to parse, both by machine and man -- and the latter is an advantage if you have nothing to hide and no need for quick read/write (where you might, for example, use SQLite). It's also a simple file-store so platform and library requirements are minimal. It's probably the easiest way to store structured configuration data and I still use it for projects unless I absolutely have to use something else.
A relatively small, simple part of the PeanutButter suite is the INI reader/writer/storage class PeanutButter.INI.INIFile. Usage is quite simple:
var ini = new INIFile("C:\\path\\to\\your\\iniFile.ini"); var someConfiguredValue = ini["colors"]["FavouriteColor"]; ini["Geometry"]["Left"] = "123"; ini.Persist();
In thesnippet above, we instantiate an INIFile class with a path to a file to use as the default persistence store. This file doesn't have to exist right now (and if it doesn't, it will be created with the Persist() call).
INIFile presents the data present in the source as a Dictionary<string, Dictionary<string, string>>, with indexing on the INIFile instance itself, making the syntax quite easy to use. Sections are created as and when you need them. Section and key names (such as "Geometry" and "Left" above) are case-insensitive to make access easier (and more compliant with the behavior of the older win32 calls for INI handling).
The parser tolerates empty lines and comments as well as empty keys (which are returned as an empty string).
Of course, you don't have to have a backing store to start with (or at all), and you can always override the output path with a parameter to Persist(). In addition, you can re-use the same INIFile, loading in a file from another path with the Load() method or loading with a pure string with the Parse() method.
Once again, the class has been developed on an as-required basis. It does much of what I want it to do (though I'd like it to persist comments on re-writing; that may come later). I hope that it can be of use to someone else too. I've lost count of how many times I've implemented an INI reader/writer. Hopefully, this is one of the last...