Handle Configuration Files Using a Portable Class’�CDataFile

Environment: VC++ 6 & Win32 -or- egcs and Linux

Almost as soon as your application becomes something more than “Hello World,” you find yourself in need of a means to persistently store configuration settings or other data. The Registry is generally a bad place for more than a few settings because of its limited size and difficulty of use, not to mention that not all platforms contain a Registry. The common method is to use some form of configuration file.

CDataFile is a C++ object whose purpose is to aid you in reading and writing to configuration files. It generally tries to be compatible with standard Windows .ini files, although it has some features that, if used, will break that compatiblity (namely, non-standard comment indicators and assignment operators).

While I’m not a huge fan of std::string (and honestly, not all that familiar with it), I felt that it would be the best choice here. It’s certainly portable and does not rely on MFC or any other third-party libraries. The lack of features in std::string made me nearly regret that choice early on, but when I did the Linux port (on a Redhat 7.2 box), the choice paid off nicely; the port went off without a hitch and the test worked as expected.

Ample documentation for the class exists within the source code and the provided test project.

Primary Features List:

  • Platform independant. The download below contains both a VC++6 project and a Linux makefile.
  • No reliance on Windows system calls. You should be able to use this code, with minor modifications, in your Linux applications as well as your Windows applications.
  • Support for multi-line comments for both sections and keys.
  • Multiple section support. This allows you to have duplicate keys, with possibly differing values (or the same for that matter).
  • Full string support allows you to have key names or values that contain spaces.
  • Easily configurable to your tastes by changing simple, well-documented flags.
  • Simple error/message reporting functionality.
  • Can read and write Windows .ini files.
  • Small, concise, and well commented.

<Jump to Download>

//
// CDataFile Class Implementation
//
// The purpose of this class is to provide a simple, full-
// featured means to store persistent data to a text file.
// It uses a simple key/value paradigm to achieve this.
// The class can read/write to standard Windows .ini files,
// yet does not rely on any Windows-specific calls. It
// should work as well in a Linux environment (with some
// minor adjustments) as it does in a Windows one.
//
// Written July, 2002 by Gary McNickle <gary@sunstorm.net>
// If you use this class in your application, credit would
// be appreciated.
//

#ifndef __CDATAFILE_H__
#define __CDATAFILE_H__

#include <vector>
#include <fstream.h>
#include <string>

// Globally defined structures, defines, & types
/////////////////////////////////////////////////////////////

// AUTOCREATE_SECTIONS
// When set, this define will cause SetValue() to create a
// new section, if the requested section does not allready
// exist.
#define AUTOCREATE_SECTIONS     (1L<<1)

// AUOTCREATE_KEYS
// When set, this define causes SetValue() to create a new
// key, if the requested key does not already exist.
#define AUTOCREATE_KEYS         (1L<<2)

// MAX_BUFFER_LEN
// Used simply as a max size of some internal buffers.
// Determines the maximum length of a line that will be read
// from or written to the file or the report output.
#define MAX_BUFFER_LEN          512


// eDebugLevel
// Used by our Report function to classify levels of
// reporting and severity of report.
enum e_DebugLevel
{
  // detailed programmatic informational messages used as
  // an aid in troubleshooting problems by programmers
  E_DEBUG = 0,
  // brief informative messages to use as an aid in
  // troubleshooting problems by production support and
  // programmers
  E_INFO,
  // messages intended to notify help desk, production
  // support, and programmers of possible issues with
  // respect to the running application
  E_WARN,
  // messages that detail a programmatic error; these are
  //  typically messages intended for help desk, production
  //  support, programmers, and occasionally users
  E_ERROR,
  // severe messages that are programmatic violations
  // that will usually result in application failure. These
  // messages are intended for help desk, production support,
  // programmers, and possibly users
  E_FATAL,
  // notice that all processing should be stopped
  // immediately after the log is written.
  E_CRITICAL
};


typedef std::string t_Str;

// CommentIndicators
// This constant contains the characters that we check
// for to determine if a line is a comment or not. Note
// that the first character in this constant is the one
// used when writing comments to disk (if the comment
// does not already contain an indicator).
const t_Str CommentIndicators = t_Str(";#");

// EqualIndicators
// This constant contains the characters that we check
// against to determine if a line contains an assignment
// ( key = value ).Note that changing these from their
// defaults ("=:") WILL affect the ability of CDataFile
// to read/write to .ini files. Also, note that the first
// to read/write to .ini files. Also, note that writing
// the values to the file. (EqualIndicators[0])
const t_Str EqualIndicators   = t_Str("=:");

// WhiteSpace
// This constant contains the characters that the
// Trim() function removes from the head and tail
// of strings.
const t_Str WhiteSpace = t_Str(" tnr");

// st_key
// This structure stores the definition of a key. A key
// is a named identifier that is associated with a value.
//  It may or may not have a comment. All comments must
// PRECEDE the key on the line in the config file.
typedef struct st_key
{
  t_Str    szKey;
  t_Str    szValue;
  t_Str    szComment;

  st_key()
  {
    szKey = t_Str("");
    szValue = t_Str("");
    szComment = t_Str("");
  }

} t_Key;

typedef std::vector<t_key> KeyList;
typedef KeyList::iterator KeyItor;

// st_section
// This structure stores the definition of a section.
// A section contains any number of keys (see st_keys),
// and may or may not have a comment. Like keys, all
// comments must precede the section.
typedef struct st_section
{
  t_Str      szName;
  t_Str      szComment;
  KeyList    Keys;

  st_section()
  {
    szName = t_Str("");
    szComment = t_Str("");
    Keys.clear();
  }

} t_Section;

typedef std::vector<t_section> SectionList;
typedef SectionList::iterator SectionItor;



/// General Purpose Utility Functions ///////////////////////
/////////////////////////////////////////////////////////////
void   Report(e_DebugLevel DebugLevel, char *fmt, ...);
t_Str  GetNextWord(t_Str& CommandLine);
int      CompareNoCase(t_Str str1, t_Str str2);
void   Trim(t_Str& szStr);
int      WriteLn(fstream& stream, char* fmt, ...);


/// Class Definitions ///////////////////////////////////////
/////////////////////////////////////////////////////////////


// CDataFile
class CDataFile
{
// Methods
public:
        // Constructors & Destructors
        ///////////////////////////////////////////////////
        CDataFile();
        CDataFile(t_Str szFileName);
  virtual    ~CDataFile();

        // File handling methods
        ///////////////////////////////////////////////////
  bool    Load(t_Str szFileName);
  bool    Save();

        // Data handling methods
        ///////////////////////////////////////////////////

        // GetValue: Our default access method. Returns the
        //  raw t_Str value. Note that this returns keys
        // specific to the given section only.
  t_Str    GetValue(t_Str szKey, t_Str szSection = t_Str(""));
        // GetString: Returns the value as a t_Str
  t_Str    GetString(t_Str szKey, t_Str szSection = t_Str(""));
        // GetFloat: Returns the value as a float
  float    GetFloat(t_Str szKey, t_Str szSection = t_Str(""));
        // GetInt: Returns the value as an int
  int      GetInt(t_Str szKey, t_Str szSection = t_Str(""));
        // GetBool: Returns the value as a bool
  bool     GetBool(t_Str szKey, t_Str szSection = t_Str(""));

        // SetValue: Sets the value of a given key. Will create the
        // key if it is not found and AUTOCREATE_KEYS is active.
  bool     SetValue(t_Str szKey, t_Str szValue,
             t_Str szComment = t_Str(""), t_Str szSection = t_Str(""));

        // SetFloat: Sets the value of a given key. Will create the
        // key if it is not found and AUTOCREATE_KEYS is active.
  bool     SetFloat(t_Str szKey, float fValue,
             t_Str szComment = t_Str(""), t_Str szSection = t_Str(""));

        // SetInt: Sets the value of a given key. Will create the
        // key if it is not found and AUTOCREATE_KEYS is active.
  bool     SetInt(t_Str szKey, int nValue,
             t_Str szComment = t_Str(""), t_Str szSection = t_Str(""));

        // SetBool: Sets the value of a given key. Will create the
        // key if it is not found and AUTOCREATE_KEYS is active.
  bool     SetBool(t_Str szKey, bool bValue,
             t_Str szComment = t_Str(""), t_Str szSection = t_Str(""));

        // Sets the comment for a given key.
  bool     SetKeyComment(t_Str szKey,
                         t_Str szComment,
                         t_Str szSection = t_Str(""));

        // Sets the comment for a given section
  bool     SetSectionComment(t_Str szSection, t_Str szComment);

        // DeleteKey: Deletes a given key from a specific section
  bool     DeleteKey(t_Str szKey,
                     t_Str szFromSection = t_Str(""));

        // DeleteSection: Deletes a given section.
  bool     DeleteSection(t_Str szSection);

        // Key/Section handling methods
        /////////////////////////////////////////////////////

        // CreateKey: Creates a new key in the requested
        // section. The Section will be created if it does
        //  not exist and the AUTOCREATE_SECTIONS bit is set.
  bool     CreateKey(t_Str szKey, t_Str szValue,
             t_Str szComment = t_Str(""), t_Str szSection = t_Str(""));
        // CreateSection: Creates the new section if it does not
        // already exist. Section is created with no keys.
  bool     CreateSection(t_Str szSection,
                         t_Str szComment = t_Str(""));
        // CreateSection: Creates the new section if it does not
        // already exist, and copies the keys passed
        // into it into the new section.
  bool     CreateSection(t_Str szSection,
                         t_Str szComment,
                         KeyList Keys);

        // Utility Methods
        /////////////////////////////////////////////////////
        // SectionCount: Returns the number of valid
        // sections in the database.
  int      SectionCount();
        // KeyCount: Returns the total number of keys,
        // across all sections.
  int      KeyCount();
        // Clear: Initializes the member variables to
        // their default states.
  void     Clear();
        // SetFileName: For use when creating the object by hand.
        // Initializes the file name so that it can be saved
        // later.
  void     SetFileName(t_Str szFileName);
        // CommentStr
        // Parses a string into a proper comment token/comment.
  t_Str    CommentStr(t_Str szComment);


protected:
        // Note: I've tried to insulate the end user from the internal
        // data structures as much as possible. This is by design.
        // Doing so has caused some performance issues (multiple
        // calls to a GetSection() function that would otherwise
        // not be necessary, etc.).
        // But, I believe that doing so will provide a safer, more
        // stable environment. You'll notice that nothing returns
        // a reference; to modify the data values, you have to call
        // member functions.
        // Think carefully before changing this.

        // GetKey: Returns the requested key (if found) from
        // the requested Section. Returns NULL otherwise.
  t_Key*   GetKey(t_Str szKey, t_Str szSection);
        // GetSection: Returns the requested section (if found),
        // NULL otherwise.
  t_Section*  GetSection(t_Str szSection);


// Data
public:
  long         m_Flags;         // Our settings flags.

protected:
  SectionList  m_Sections;      // Our list of sections
  t_Str        m_szFileName;    // The filename to write to
  bool         m_bDirty;        // Tracks whether or not data has
                                // changed.
};


#endif

Downloads

Download source – 15Kb

More by Author

Get the Free Newsletter!

Subscribe to Data Insider for top news, trends & analysis

Must Read