Click here to Skip to main content
Click here to Skip to main content

API-Less INI File Wrapper

By , 5 Nov 2008
 

Introduction

Using Windows APIs to read and write (WritePrivateProfileString, GetPrivateProfileString ) INI files is easy, but if you have to get or write data on your app many times, it could cause performance issues because the functions load the INI files on each call.

So, this class will help you managing ini data on memory.

Background

This class does not use the classical INI get or put methods. It loads an INI file on initialize, uses Regular Expressions to parse data, and uses a dictionary for managing section names and data.

Regular Expressions:

static readonly Regex regRemoveEmptyLines =
    new Regex
    (
        @"(\s*;[\d\D]*?\r?\n)+|\r?\n(\s*\r?\n)*", 
        RegexOptions.Multiline | RegexOptions.Compiled
    );

static readonly Regex regParseIniData =
    new Regex
    (
        @"
        (?<IsSection>
            ^\s*\[(?<SectionName>[^\]]+)?\]\s*$
        )
        |
        (?<IsKeyValue>
            ^\s*(?<Key>[^(\s*\=\s*)]+)?\s*\=\s*(?<Value>[\d\D]*)$
        )",
        RegexOptions.Compiled | 
        RegexOptions.IgnoreCase | 
        RegexOptions.IgnorePatternWhitespace
    );

Dictionary type:

Dictionary<string, NameValueCollection> data = 
    new Dictionary<string,NameValueCollection>();

Actually, I did not search much to see if there is something like this already. I needed it and I wrote it. And, I thought that someone might find this useful.

Using the code

Initializing:

//Creates an empty ini document, you can add sections,
//keys and values dynamically. And you can save it anytime.
TA_INIDocument ini = new TA_INIDocument();

//Initializes an ini file, you can change its data and save
//it anytime
TA_INIDocument ini = new TA_INIDocument("C:\\sample.ini");

//Initializes an ini file with encoding. Sometimes ini files
//could have unicode data
TA_INIDocument ini = 
    new TA_INIDocument("C:\\sample.ini", Encoding.Unicode);

//Initializes ini data from stream
Stream iniStream;
TA_INIDocument ini = new TA_INIDocument(iniStream);

//Initializes ini data from stream with encoding
Stream iniStream;
TA_INIDocument ini =
    new TA_INIDocument(iniStream, Encoding.Unicode);

Getting and setting values:

TA_INIDocument iniDoc;

//Getting key_value collection of defined section
NameValueCollection keysAndValues = iniDoc["sectionName"];
//Getting string value of defined key and section
string value = iniDoc["sectionName"]["keyName"];
string value = iniDoc["sectionName", "keyName"];
//Setting string value of defined key
iniDoc["sectionName"]["keyName"] = "newValue";
iniDoc["sectionName", "keyName"] = "newValue";
//Getting and Setting object value of defined key and section
object value = iniDoc["sectionName", "keyName", typeof(Int32)];
iniDoc["sectionName", "keyName", typeof(Rectangle)] =
    new Rectangle(0, 0, 200, 300);
//Getting and setting specific type values excepts string
//Primitive Types (included Decimal and DateTime)
//TA_INIDocument.Get{PrimitiveTypeName}(sectionName, keyName, [defaultValue])

bool bValue = 
    iniDoc.GetBoolean("sectionName", "keyName");

bool bValue = 
    iniDoc.GetBoolean("sectionName", "keyName", true);

iniDoc.SetValue("sectionName", "keyName", bValue);

DateTime dtValue = 
    iniDoc.GetDateTime("sectionName", "keyName");

DateTime dtValue = 
    iniDoc.GetDateTime("sectionName", "keyName", DateTime.MaxValue);

iniDoc.SetValue("sectionName", "keyName", dtValue);
//Other types (required that the type has TypeConverterAttribute)
//TA_INIDocument.GetValue<T>(sectionName, keyName, [T:defaultValue])

Rectangle rtValue =
    iniDoc.GetValue<Rectangle>("sectionName", "keyName");

Rectangle rtValue =
    iniDoc.GetValue<Rectangle>("sectionName", "keyName", Rectangle.Empty);

iniDoc.SetValue("sectionName", "keyName", rtValue);

Helper functions:

//Helper Properties and Functions
TA_INIDocument iniDoc;
//Getting All Section Names
string[] sectionNames = iniDoc.SectionNames;
//Getting All Key Names of a Section
string[] keyNames = iniDoc.KeyNames("sectionName");
//Getting All Values of a Section
string[] allValues = iniDoc.SectionValues("sectionName");
//Check if section name exits
if (iniDoc.HasSection("sectionName"))
    Application.DoEvents();
//Check if key name exits of specified section
if (iniDoc.HasKey("sectionName", "keyName"))
    Application.DoEvents();

Saving all:

//Saving all data
TA_INIDocument iniDoc;
Stream iniStream;

iniDoc.Save(iniStream);
//or
iniDoc.Save(iniStream, Encoding.Unicode);
//or
iniDoc.Save("C:\\sample.ini");
//or
iniDoc.Save("C:\\sample.ini", Encoding.Unicode);

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Tolgahan ALBAYRAK
Software Developer (Senior)
Turkey Turkey
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralRe: Terminal Services and PerformancememberTolgahan ALBAYRAK10 Nov '08 - 11:18 
Actually, (i think) if .net framework works on t.s, and logged in account has file reading and writing permissions then, this code will probably work without any error or exception.
 
pls, fix me if i am wrong.
 
I havent been found the mean of life yet.

QuestionPurpose?memberDenis Vuyka10 Nov '08 - 8:43 
This is a very interesting article. However I cannot understand the scenario where it works more efficient than native Win API. When dealing with massive updates your sample won't manage flushing the data. I also tried .net approach in the past but got no luck neither with performance nor reliability. Here's what helped me http://dvuyka.spaces.live.com/blog/cns!305B02907E9BE19A!174.entry
Also don't forget that JIT will try producing native code so you won't produce less processor instructions than doing the same via native calls Wink | ;)
 
Kind regards, Denis

AnswerRe: Purpose?memberTolgahan ALBAYRAK10 Nov '08 - 10:22 
Smile | :) Yes, i am exactly sure that native calls or native code processing methods works better than any other methods what builded on native.
 
But, we both sure that although (4 + 4) equals to (4 * 2), in math arch. of cpu, (4 * 2) process will get higher processor cost than the other.
 
So, my point is the technique. The Native Calls for INI files, loads specified file on each call and parses the data untill find what you looked for..
 
This class reads the file only once, then all other updates or modifications executes on allocated memory. Before exit your app, saves it once. And that is all..
 
I havent been found the mean of life yet.

GeneralRe: Purpose?memberDenis Vuyka10 Nov '08 - 21:06 
Yes as for the technique this is a good article. Setting an excelent rank for it Smile | :)
 
Kind regards, Denis

GeneralRe: Purpose?memberTolgahan ALBAYRAK10 Nov '08 - 21:56 
Smile | :) thank you
 
I havent been found the mean of life yet.

Generalregular expressionsmemberE. del Ayre9 Nov '08 - 20:31 
im a sucker for good technique...
since, you just showed me how clueless i am re: regular expressions...
then you got my 5... great work...
 
think fast, be brave and dont stop.

GeneralRe: regular expressionsmemberTolgahan ALBAYRAK10 Nov '08 - 9:31 
thank you.
 
I havent been found the mean of life yet.

QuestionPerformance comparison -- benchmark app?memberbolivar1237 Nov '08 - 4:00 
I'd be interested in a performance comparison of your INI wrapper compared to using Win32 functions. Perhaps a sample application that exercises and benchmarks your wrapper class functions and then does the same operations using the Win32 APIs.
 
Please quantify how much faster your wrapper class is versus using the Windows functions.
AnswerRe: Performance comparison -- benchmark app?memberTolgahan ALBAYRAK7 Nov '08 - 6:54 
Here is the performance test result.
 
@@@@@@@@@ Performance Test Starting @@@@@@@@@
 
Testing TA_INIDocument
 
Test Started At : 19:43:12.2656250
 
Instance Created At : 19:43:12.2656250
 
Adding 100 Sections, 100 Keys And Values to per section (10,000 Times)
 
Addition Completed At : 19:43:12.3125000
 
Reading 100 Sections, 100 Keys And Values of per section (10,000 Times)
 
Reading Completed At : 19:43:12.3437500
 
Saving...
 
Saving Completed At : 19:43:12.3906250
 
Re-Instancing...
 
Re-Instancing Completed At : 19:43:12.4843750
 
Test Completed. Total Time for TA_INIDocument : 00:00:00.2187500
 
####################################################
 
Testing Win API
 
Test Started At : 19:43:12.4843750
 
Instance Created At : 19:43:12.4843750
 
Adding 100 Sections, 100 Keys And Values to per section (10,000 Times)
 
Addition Completed At : 19:43:16.9375000
 
Reading 100 Sections, 100 Keys And Values of per section (10,000 Times)
 
Reading Completed At : 19:43:20.7187500
 
Saving... (No Save Required)
 
Saving Completed At : 19:43:20.7187500
 
Re-Instancing...
 
Re-Instancing Completed At : 19:43:20.7187500
 
Test Completed. Total Time for Win API : 00:00:08.2343750
 
@@@@@@@@@@@@ Performance Test End @@@@@@@@@@
 

 
Results :
 
TA_INIDocument = 00:00:00.2187500 (218 ms)
Win API = 00:00:08.2343750 (8s 234ms)
 

If you want to do your own test, you can download this test project from :
 
http://www.cdsnettr.com/TA_INIDocumentPerfTest.zip[^]
 
I havent been found the mean of life yet.

GeneralRe: Performance comparison -- benchmark app?mvpJohn Simmons / outlaw programmer8 Nov '08 - 1:52 
Realistically, INI settings are typically read/written once in a while. While your test of 10,000 cycles yielded a 4-second savings in time, the premise that someone would read/write this often is wholly unrealistic.
 

"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997
-----
"...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001

GeneralRe: Performance comparison -- benchmark app?memberTolgahan ALBAYRAK8 Nov '08 - 2:47 
Actually, this class' aim is reading and writing ini files without Windows APIs. Because, although u can run .net framework some other os like linux, if you don't use only managed codes this will raise errors.
 
I know when and why i use INI files. By the way, bolivar123 just requested a performance test and i answered them. So, i didnt think if i am realistic.
 
I havent been found the mean of life yet.

GeneralRe: Performance comparison -- benchmark app?member Randor 14 Nov '08 - 8:22 
Your benchmark is seriously flawed. Your C# version is simply writing to memory 10,000 times in a tight loop and then writing to the disk one time. The Win32 version writes to disk 10,000 times probably hitting the kernel I/O cache buffer a few times along the way. Nevertheless the benchmark is meaningless in its current state.
 
I modified your code so that it saves after each key change and the C# version was slower by several magnitudes.
 
Best Wishes,
-David Delaune

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 5 Nov 2008
Article Copyright 2008 by Tolgahan ALBAYRAK
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid