Working with INI Configuration Files Cross-Platform (Win32/Un*x, MBCS/Unicode)

SimpleIni

Cross platform programming can be painful, especially when it comes to simple tasks that you don't want to think about because they aren't your main problem. Unfortunately, it sometimes takes longer than it should to solve these tasks. One of these problems is loading configuration files. Although cross-platform XML libraries are available, if the user of your software is going to directly modify the file, it can be painful. The old Windows INI configuration file is a lasting file format that is easy for the user to understand and hard to get wrong.

This component is a C++ class that provides the ability to load an INI-style configuration file on both Windows and Linux/Unix. It is fast, simple, and source code using this component will compile unchanged on either OS. It supports both MBCS and Unicode builds on Windows, and can load UTF-8 or MBCS files on all platforms.

Features

  • Public domain, free use in all software (including GPL and commercial)
  • Multi-platform (Windows 95/98/ME/NT/2K/XP/2003, Linux, Unix)
  • Loads and saves INI-style configuration files
  • Liberal acceptance of file format
    • Key/Values with no section
    • Removal of whitespace around sections, keys, and values
  • Optional case-insensitive sections and keys (for ASCII characters only)
  • Supports both char or wchar_t programming interfaces
  • Supports both MBCS (system locale) and UTF-8 file encodings
  • System locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file
  • Support for non-ASCII characters in section, keys, values, and comments
  • Support for non-standard character types or file encodings via user-written converter classes
  • Support for adding/modifying values programmatically

Usage Summary

  1. Declare an instance of the appropriate class. Note that the following definitions are just shortcuts for commonly used types. Other types (for example, PRUnichar, unsigned short, unsigned char) are also possible.
  2. Interface Case-sensitive Typedef
    char No CSimpleIniA
    char Yes CSimpleIniCaseA
    wchar_t No CSimpleIniW
    wchar_t Yes CSimpleIniCaseW
  3. Call LoadFile() to load and parse the INI configuration file.
  4. Use the following functions to access the file's data:
  5. GetAllSections Return all section names
    GetAllKeys Return all key names for a section
    GetSection Return all key names and values in a section
    GetSectionSize Return the number of keys in a section
    GetValue Return a value for a section & key
    SetValue Add or update a value for a section & key
  6. Call SaveFile() to save the INI configuration file (if necessary)

Notes

  • The collation (sorting) order used for sections and keys returned from iterators is NOT DEFINED. If collation order of the text is important, you should do it yourself by either supplying a replacement SI_STRCMP class or by sorting the strings external to this library.
  • Linux/Unix systems must also compile and link ConvertUTF.c.
  • To load a UTF-8 file on Windows and expose it with SI_CHAR == char, you need to define SI_USING_WIN32_CHAR_FOR_UTF8.

Download

The current version of SimpleIni is 1.5. The home page is http://code.jellycan.com/SimpleIni/.

Example Program

This program loads an INI file and dumps out all of the data from it.

#include "SimpleIni.h"

int main(int argc, char * argv[])
{
   setlocale(LC_CTYPE, "");

   if (argc < 2) {
      printf(
         "Usage: %s iniFile [useMBCS]\n"
         "   Pass 1 for useMBCS to use system locale file
             encoding\n",
         argv[0]);
      return 1;
   }
   bool bIsUtf8 = (argc < 3) ? true : *argv[2] != '1';

   const char * pDebug;
   printf("Dumping file using char version:\n");
   CSimpleIniA ini;
   if (0 != ini.LoadFile(argv[1], bIsUtf8)) {
      printf("Failed to open: %s\n", argv[1]);
       exit(1);
   }
   CSimpleIniA::TNames sections;
   ini.GetAllSections(sections);
   CSimpleIniA::TNames::const_iterator j = sections.begin();
   for ( ; j != sections.end(); ++j ) {
      printf("\n");
      if (*j[0]) {
         printf("[%s]\n", pDebug = *j);
      }
      const CSimpleIniA::TKeyVal * pSection = ini.GetSection(*j);
      if (pSection) {
         CSimpleIniA::TKeyVal::const_iterator i =
            pSection->begin();
         for ( ;i != pSection->end(); ++i) {
            printf("%s=%s\n", pDebug = i->first, i->second);
         }
      }
   }

   printf("\nQuerying section: [standard]\n");
   CSimpleIniA::TNames keys;
   ini.GetAllKeys("standard", keys);
   CSimpleIniA::TNames::const_iterator k = keys.begin();
   for ( ; k != keys.end(); ++k ) {
      printf("Key: %s\n", pDebug = *k);
   }

   pDebug = ini.GetValue("standard", "foo", 0);
   printf("\nValue of standard::foo is '%s'\n", pDebug ?
           pDebug : "(null)");

   ini.SetValue("standard", "foo", "wibble");
   pDebug = ini.GetValue("standard", "foo", 0);
   printf("Value of standard::foo is now '%s'\n", pDebug ?
           pDebug : "(null)");

   FILE * fp = fopen("testsi-char.ini", "wb");
   if (fp) {
      ini.SaveFile( fp, "; testsi.cpp test output (char)" SI_NEWLINE );
      fclose(fp);
   }

   return 0;
}

Disclaimer

This code is released as public domain. You can do with it whatever you like: use it, modify it, distribute it, sell it, delete it, or send it to your mother-in-law. I make no promises or guarantees that this code will work correctly or at all. Use it completely at your own risk.



About the Author

Brodie Thiesfield

Programming to eat. Donations of canned food accepted.

Downloads