Greene's Programming Pages
C++

"CfgReader" — A Configuration File Parser Using C++ and STL

Greetings, fellow programmers!

As a professional programmer looking for "the best way" to do things, I'm frequently trying out new ways to do things, and frequently modifying my code with new ways I've found.

For example, see my discussion regarding making file i/o more efficient by replacing your use of C++'s fstream functions with C's FILE* functions. For over three years I wrote all of the file i/o in my programs using fstream. I got away with this, because, in fact, I was using the Borland 5.0 C++ compiler all of that time, but when I "upgraded" to Borland's 5.5 version, my program's starting running much slower. In digging into the problem, I also tried out Visual C++ 6.0. Now, the reason for my wanting to upgrade from Borland 5.0 was my increasing use of STL, and STL was still pretty new when the Borland 5.0 compiler came out, so the compiler frequently generated slow code with STL (as well as having certain bugs, such as using ostream with maps). If I wanted to use STL (and I do), I clearly needed to upgrade. I wrote some test programs that do almost nothing more than straight file i/o, using either fstream or FILE*, and found to my big surprise that with Borland 5.5 and Visual C++ 6.0, the fstream could take anywhere from 2½ to 3 times longer than using FILE*. (Borland 5.0's fstream was only something like 10% slower than FILE*, which is why it was never a big enough difference that it stood out to me earlier.)

The C++ books I learned C++ from taught me to use fstream for file i/o. Yeah, yeah, okay. I like C++, too, but this kind of performance hit is intolerable. I write programs that need to crunch through data. I know some programmers don't care about this, only dealing with a few thousand records or so, but I'm dealing with millions of records at a time, and when my program used to run for, say, 5 minutes when compiled by Borland 5.0, and now takes almost 15 minutes to run, that means my program user has lost 10 minutes of processing time for a single run.

Not acceptable.

Thus, for me the choice was clearly obvious: Switch to FILE* for file i/o, the niceties of C++'s fstream be damned. Sorry, but that's reality. There's the "hacked" way, the "elegant" way, and then there's the practical way, and in the real world the practical way will be some fluctuation between the two poles.

So when you see mixtures of C, C++, and STL in my code, please be aware that this has been produced by an evolution of programming based on shooting for the practical in both processing efficiency and programming efficiency, and these sometimes conflict with each other. Where you might think, "Hey, isn't this kind of inconsistent? Here you've got some STL strings, but over here you're 'reverting' to C char arrays." It could, of course, be something I simply haven't "gotten around to" changing in my own personal evolution of my code. But on the other hand, please be aware that I am writing my code always with processing efficiency on my list of coding priorities, and if I feel that I want to stick with a char array in particular sections of code, I do so very purposely — so sometimes the use of char array is a deliberate judgment call.

So now, on to the configuration file parser code...

As I have intimated, I primarily write programs related to crunching data, with little user interaction. These are console applications that are run from the command line in a console window (and that can also be run in a batch process). (With these kinds of programs, though on rare occasions I may use a Win 32 function here and there, such as to find the current amount of available memory, I try to stick with fairly compatible, standard code — another item on my list of coding priorities.)

This configuration file parser is an evolution of substantial STL mutations from an earlier parser I had written which used only char arrays. Using STL, this parser is far more elegant (believe me) and safer than my previous parser. I use this class to read parameter information entered by the user into a Windows INI style configuration file (using any text editor). I have overloaded some of the functions to accept some combination of char arrays or strings, or arrays of char arrays or STL vectors or STL deques of strings.

To see an example of using this CfgReader class, take a look at my "de-duper" program.

A C++ programmer,
Todd S. Greene
<tgreene@usxchange.net>
(Oct. 13, 2000)


//==============================================================================
// datetime: 9/20/00 10:06:40 am EST
// Copyright 2000 Todd S. Greene
// https://members.tripod.com/toddsgreene/
//
// This code is hereby released to the public domain.
//
// Your actual use of this code is your responsiblity, not mine. (In other
// words, if you choose to use this code, then you are liable for results,
// not me. No one has the right to sue me for anything, based merely on using
// some information that I have provided on a purely non-commercial basis.)
//==============================================================================

#ifndef _CfgSTL_h_
#define _CfgSTL_h_

#include <string>
#include <deque>
#include <vector>
#include <iostream>
#include <fstream>
using namespace std;

#include <sys\stat.h>


class CfgReader
   {
private:
   deque<string> lines;
   char* fs_name;
   char* cstr;

public:
   CfgReader(const char*);
   CfgReader(const string);
   ~CfgReader();

   void constructor_function() throw(int);

// important note: must use "const string" as the argument here and not "const
//    string&" because there are cases where we would want to immediately feed
//    a result string back into the function, and if you used "string&"
//    (referring to the original) instead of "string" (which actually works on a
//    copy), then when you clear out the output variables in preparation for
//    putting information into then you would actually wipe out the string you
//    are working on.
   virtual unsigned short GetParmParts(string&, string&, const string);

   virtual unsigned short GetParmArgs(vector<string>&, const string, const char);
   virtual unsigned short GetParmArgs(deque<string>&, const string, const char);
   virtual unsigned short GetParmItems(vector<string>&, const string, const char);
   virtual unsigned short GetParmItems(deque<string>&, const string, const char);

   deque<string> GetLines();

   virtual unsigned short GetParmValue(char*, const char*, const char*);
   virtual unsigned short GetParmValue(string&, const char*, const char*);

   virtual unsigned short GetParmValue(char*, const char*);
   virtual unsigned short GetParmValue(string&, const char*);

   virtual unsigned short GetParmValues(char**, unsigned int, const char*, const char*);
   virtual unsigned short GetParmValues(vector<string>&, const char*, const char*);
   virtual unsigned short GetParmValues(deque<string>&, const char*, const char*);

   virtual unsigned short GetSection(char**, unsigned int, const char*);
   virtual unsigned short GetSection(vector<string>&, const char*);
   virtual unsigned short GetSection(deque<string>&, const char*);

   virtual unsigned short trimsides(string&);
   };

//##############################################################################

CfgReader::CfgReader(const char* filename)
   {
   fs_name = new char[strlen(filename)+1];
   strcpy(fs_name, filename);
   constructor_function();
   }

CfgReader::CfgReader(string filename)
   {
   fs_name = new char[filename.size()+1];
   strcpy(fs_name, filename.c_str());
   constructor_function();
   }

void CfgReader::constructor_function() throw(int)
   {
//cout << "fs_name = |" << fs_name << "|" << endl;
   // get file size:
   struct stat statbuf;
   if (stat(fs_name, &statbuf))
      {
      cerr << "\nfatal error: could not get cfg file size of \"" << fs_name << "\"" << endl;
      throw -1;
      }
   unsigned long ul_filesize = statbuf.st_size;

   const unsigned long min_filesize = 5;
   if (ul_filesize < min_filesize)
      {
      cerr << "\nfatal error: cfg file size < " << min_filesize << "bytes (min allowed)" << endl;
      throw -1;
      }
   const unsigned long max_filesize = 262144;
   if (ul_filesize > max_filesize)
      {
      cerr << "\nfatal error: cfg file size > " << max_filesize << "bytes (max allowed)" << endl;
      throw -1;
      }

   ifstream fs;
   fs.open(fs_name, ios::binary);
   if (!fs)
      {
      cerr << "\nfatal error: could not open cfg file \"" << max_filesize << "\"" << endl;
      throw -1;
      }

   // create a buffer of filesize, and read the entire file into it:
   const unsigned long sz_buf = ul_filesize + 1;
   char* buf = new char[sz_buf];
   if (!fs.read(buf, ul_filesize))
      {
      cerr << "\nfatal error: could not read cfg file \"" << fs_name << "\"" << endl;
      throw -1;
      }

   unsigned long bytes_read = fs.gcount();
//cout << "bytes_read = " << bytes_read << endl;
   if (bytes_read != ul_filesize)
      {
      cerr << "\nfatal error: read byte count does not match file size" << endl;
      cerr << "             (file size = " << ul_filesize << ", bytes read = " << bytes_read << ")" << endl;
      throw -1;
      }
//{
//unsigned long i;
//*(buf+fs.gcount()) = '\0';
//for (i = 0; i < fs.gcount(); i++) {
//cout << "*(buf+" << i << ") = ";
//if ((*(buf+i) != '\x0D') && (*(buf+i) != '\x0A')) { cout << "\'" << *(buf+i) << "\'"; }
//else {
//if (*(buf+i) == '\x0D') { cout << "{CR}"; }
//else { cout << "{LF}"; }
//}
//cout << endl;
//}
//}

   //===========================================================================
   // setup variables for primary loop:
   //---------------------------------------------------------------------------
   // create a pointer to the last byte in the buffer:
   char* ptr_eof = buf + sz_buf - 1;
   // if last byte in buffer is "ctrl-Z", back up one byte (i.e., eliminate it):
   if (*(ptr_eof-1) == '\x1A')
      { ptr_eof--; }
   // if "last byte" in buffer is {LF}, back up to it and use it as
   // "last byte"; otherwise, place a {LF} in current "last byte":
   if (*(ptr_eof-1) == '\x0A')
      { ptr_eof--; }
   else
      { *(ptr_eof) = '\x0A'; }

   char* ptr_sol = buf;       // pointer to "start of line"
   char* ptr_eol = buf - 1;   // pointer to "end of line"
   unsigned long len_line;    // length of line
   unsigned long maxlen_line = 0;
   //===========================================================================

   //===========================================================================
   // primary loop through data lines of file:
   //---------------------------------------------------------------------------
//cout << "entering primary loop" << endl;
   while (true)
      {
      unsigned long bytes_remaining = ptr_eof - ptr_eol;
      ptr_eol = (char*)memchr(ptr_sol, '\x0A', bytes_remaining);

      len_line = ptr_eol - ptr_sol;
      if (len_line > 0)
         {
         if (*(ptr_sol+len_line-1) == '\x0D')  // since binary data
            { len_line--; }
         }

      //------------------------------------------------------------------------
      // strip "comments" from line
      // (note that this "for" loop will not execute if len_buf = 0):
      unsigned long sub;
      for (sub = 0; sub < len_line; sub++)
         {
         if (*(ptr_sol+sub) == '/')
            {
            if (sub < (len_line - 1))  // this will never be true if len_line = 1
               {
               if (*(ptr_sol+sub+1) == '/')
               len_line = sub;
               break;
               }
            }
         else
            {
            if (*(ptr_sol+sub) == ';')
               {
               len_line = sub;
               break;
               }
            }
         }
      //------------------------------------------------------------------------

      //------------------------------------------------------------------------
      // strip spaces off the end of the line:
      for (; len_line > 0;)
         {
         if (*(ptr_sol+len_line-1) != ' ')
            { break; }
         else
            { len_line--; }
         }
      *(ptr_sol+len_line) = '\0';  // null-terminate the string

      // strip spaces from beginning of line:
      while (*(ptr_sol) == ' ')
         {
         ptr_sol++;
         len_line--;
         }
      //------------------------------------------------------------------------
//cout << "ptr_sol = |" << ptr_sol << "|" << endl;
      if (len_line > 0)
         {
         lines.push_back(ptr_sol);
         if (len_line > maxlen_line)
            { maxlen_line = len_line; }
         }

      if (ptr_eol == ptr_eof)
         { break; }

      ptr_sol = ptr_eol;
      ptr_sol++;
      }  // end of primary loop through data lines of file

   delete[] buf;
   //===========================================================================
//cout << "finished primary loop" << endl;

//cout << endl;
//cout << "maxlen_line = " << maxlen_line << endl;

//unsigned long i;
//for (i = 0; i < lines.size(); i++) { cout << "lines.at(" << i << ") = |" << lines.at(i) << "|" << endl; }

//   char* str_temp = new char[maxlen_line+1];
//   unsigned long len_str_temp;

//   for (i = 1; i < lines.size(); i++)
//      {
//      string str_parm_name;
//      string str_parm_value;
//      GetParmParts(str_parm_name, str_parm_value, lines.front());
//cout << "str_parm_name = |" << str_parm_name << "|" << endl;
//cout << "str_parm_value = |" << str_parm_value << "|" << endl;
//      }
   cstr = new char[2048];
   }

//##############################################################################

CfgReader::~CfgReader()
   {
   delete[] cstr;
   delete[] fs_name;
   }

//##############################################################################

deque<string> CfgReader::GetLines()
   {
//cout << "lines.size() = " << lines.size() << endl;
//unsigned long i;
//for (i = 0; i < lines.size(); i++) { cout << "lines.at(" << i << ") = |" << lines.at(i) << "|" << endl; }
   return lines;
   }

//##############################################################################

unsigned short CfgReader::GetParmValue(char* parmvalue, const char* section, const char* parm)
   {
   *(parmvalue) = '\0';
   if (lines.size() < 2)
      { return 0; }

//cout << "section = |" << section << "|" << endl;
//cout << "parm = |" << parm << "|" << endl;

   string StrSection = section;
   StrSection = "[" + StrSection + "]";
   unsigned int i;
   for (i = 0; i < lines.size(); i++)
      {
      if (lines.at(i).substr(0,StrSection.size()) == StrSection)
         { break; }
      }
   if (i > (lines.size() - 2))
      { return 0; }
   i++;

   string StrParm;
   string StrParmvalue;
   for (; i < lines.size(); i++)
      {
//cout << "lines.at(i) = |" << lines.at(i) << "|" << endl;
      if (lines.at(i).at(0) == '[')
         { return 0; }

      if (GetParmParts(StrParm, StrParmvalue, lines.at(i)))
         {
         if (StrParm == parm)
            { break; }
         }
      }

   strcpy(parmvalue, StrParmvalue.c_str());
//cout << "parmvalue = |" << parmvalue << "|" << endl;
   return StrParmvalue.size();
   }

unsigned short CfgReader::GetParmValue(string& parmvalue, const char* section, const char* parm)
   {
   parmvalue.assign("");
   if (lines.size() < 2)
      { return 0; }

   string StrSection = section;
   StrSection = "[" + StrSection + "]";
   unsigned int i;
   for (i = 0; i < lines.size(); i++)
      {
      if (lines.at(i).substr(0,StrSection.size()) == StrSection)
         { break; }
      }
   if (i > (lines.size() - 2))
      { return 0; }
   i++;

   string StrParm;
   string StrParmvalue;
   for (; i < lines.size(); i++)
      {
      if (lines.at(i).at(0) == '[')
         { return 0; }

      if (GetParmParts(StrParm, StrParmvalue, lines.at(i)))
         {
         if (StrParm == parm)
            { break; }
         }
      }

   parmvalue.assign(StrParmvalue);
   return StrParmvalue.size();
   }

unsigned short CfgReader::GetParmValue(char* parmvalue, const char* parm)
   {
   *(parmvalue) = '\0';
   if (lines.size() < 2)
      { return 0; }

   string StrParm;
   string StrParmvalue;
   unsigned int i;
   for (i = 0; i < lines.size(); i++)
      {
      if (GetParmParts(StrParm, StrParmvalue, lines.at(i)))
         {
         if (StrParm == parm)
            { break; }
         }
      }

   strcpy(parmvalue, StrParmvalue.c_str());
   return StrParmvalue.size();
   }

unsigned short CfgReader::GetParmValue(string& parmvalue, const char* parm)
   {
   parmvalue.assign("");
   if (lines.size() < 2)
      { return 0; }


   string StrParm;
   string StrParmvalue;
   unsigned int i;
   for (i = 0; i < lines.size(); i++)
      {
      if (GetParmParts(StrParm, StrParmvalue, lines.at(i)))
         {
         if (StrParm == parm)
            { break; }
         }
      }

   parmvalue.assign(StrParmvalue);
   return StrParmvalue.size();
   }

//##############################################################################

unsigned short CfgReader::GetParmValues(char** VStr, unsigned int sz_VStr, const char* section, const char* parm)
   {
   unsigned sub;
   for (sub = 0; sub < sz_VStr; sub++)
      { *(VStr[sub]) = '\0'; }  // empty the array
   if (lines.size() < 2)
      { return 0; }

   string StrSection = section;
   StrSection = "[" + StrSection + "]";
   unsigned int i;
   for (i = 0; i < lines.size(); i++)
      {
      if (lines.at(i).substr(0,StrSection.size()) == StrSection)
         { break; }
      }
   if (i > (lines.size() - 2))
      { return 0; }
   i++;

   string StrParm;
   string StrParmvalue;
   sub = 0;
   for (; i < lines.size(); i++)
      {
      if (lines.at(i).at(0) == '[')
         { break; }

      if (GetParmParts(StrParm, StrParmvalue, lines.at(i)))
         {
         if (StrParm == parm)
            {
            if (StrParmvalue.size() > 0)
               {
               strcpy(VStr[sub], StrParmvalue.c_str());
               sub++;
               if (sub == sz_VStr)
                  { return sub; }
               }
            }
         }
      }

   return sub;
   }

unsigned short CfgReader::GetParmValues(vector<string>& VStr, const char* section, const char* parm)
   {
   VStr.erase(VStr.begin(), VStr.end());  // empty the vector
   if (lines.size() < 2)
      { return 0; }

   string StrSection = section;
   StrSection = "[" + StrSection + "]";
   unsigned int i;
   for (i = 0; i < lines.size(); i++)
      {
      if (lines.at(i).substr(0,StrSection.size()) == StrSection)
         { break; }
      }
   if (i > (lines.size() - 2))
      { return 0; }
   i++;

   string StrParm;
   string StrParmvalue;
   for (; i < lines.size(); i++)
      {
      if (lines.at(i).at(0) == '[')
         { break; }

      if (GetParmParts(StrParm, StrParmvalue, lines.at(i)))
         {
         if (StrParm == parm)
            {
            if (StrParmvalue.size() > 0)
               { VStr.push_back(StrParmvalue); }
            }
         }
      }

   return VStr.size();
   }

unsigned short CfgReader::GetParmValues(deque<string>& VStr, const char* section, const char* parm)
   {
//cout << "\nin CfgReader::GetParmValues()" << endl;
   VStr.erase(VStr.begin(), VStr.end());  // empty the deque
   if (lines.size() < 2)
      { return 0; }

   string StrSection = section;
   StrSection = "[" + StrSection + "]";
//cout << "StrSection = " << StrSection << endl;
   unsigned int i;
   for (i = 0; i < lines.size(); i++)
      {
//cout << "lines.at(" << i << ") = |" << lines.at(i) << "|" << endl;
      if (lines.at(i).substr(0,StrSection.size()) == StrSection)
         { break; }
      }

   if (i > (lines.size() - 2))
      { return 0; }

//cout << "found " << StrSection << " section" << endl;
   i++;
   string StrParm;
   string StrParmvalue;
   for (; i < lines.size(); i++)
      {
//cout << "lines.at(" << i << ") = |" << lines.at(i) << "|" << endl;
      if (lines.at(i).at(0) == '[')
         { break; }

      if (GetParmParts(StrParm, StrParmvalue, lines.at(i)))
         {
         if (StrParm == parm)
            {
            if (StrParmvalue.size() > 0)
               { VStr.push_back(StrParmvalue); }
            }
         }
      }

//cout << "VStr.size() = " << VStr.size() << endl;
   return VStr.size();
   }

//##############################################################################

unsigned short CfgReader::GetSection(char** VStr, unsigned int sz_VStr, const char* section)
   {
   unsigned sub;
   for (sub = 0; sub < sz_VStr; sub++)
      { *(VStr[sub]) = '\0'; }  // empty the array
   if (lines.size() < 2)
      { return 0; }

   string StrSection = section;
   StrSection = "[" + StrSection + "]";
   unsigned int i;
   for (i = 0; i < lines.size(); i++)
      {
      if (lines.at(i).substr(0,StrSection.size()) == StrSection)
         { break; }
      }
   if (i > (lines.size() - 2))
      { return 0; }
   i++;

   sub = 0;
   for (; i < lines.size(); i++)
      {
      if (lines.at(i).at(0) == '[')
         { break; }
      strcpy(VStr[sub], lines.at(i).c_str());
      sub++;
      if (sub == sz_VStr)
         { break; }
      }

   return sub;
   }

unsigned short CfgReader::GetSection(vector<string>& VStr, const char* section)
   {
   VStr.erase(VStr.begin(), VStr.end());  // empty the vector
   if (lines.size() < 2)
      { return 0; }

   string StrSection = section;
   StrSection = "[" + StrSection + "]";
   unsigned int i;
   for (i = 0; i < lines.size(); i++)
      {
      if (lines.at(i).substr(0, StrSection.size()) == StrSection)
         { break; }
      }
   if (i > (lines.size() - 2))
      { return 0; }
   i++;

   for (; i < lines.size(); i++)
      {
      if (lines.at(i).at(0) == '[')
         { break; }
      VStr.push_back(lines.at(i));
      }

   return VStr.size();
   }

unsigned short CfgReader::GetSection(deque<string>& VStr, const char* section)
   {
   VStr.erase(VStr.begin(), VStr.end());  // empty the deque
   if (lines.size() < 2)
      { return 0; }

   string StrSection = section;
   StrSection = "[" + StrSection + "]";
   unsigned int i;
   for (i = 0; i < lines.size(); i++)
      {
      if (lines.at(i).substr(0, StrSection.size()) == StrSection)
         { break; }
      }
   if (i > (lines.size() - 2))
      { return 0; }
   i++;

   for (; i < lines.size(); i++)
      {
      if (lines.at(i).at(0) == '[')
         { break; }
      VStr.push_back(lines.at(i));
      }

   return VStr.size();
   }

//##############################################################################

unsigned short CfgReader::trimsides(string& Str)
   {
   if (Str.size() == 0)
      { return 0; }

   strcpy(cstr, Str.c_str());
   unsigned int len_cstr = 0;
   char* cstr_i = cstr;

   // skip leading whitespace:
   while ((*(cstr_i) == ' ') || (*(cstr_i) == '\t'))
      {
      cstr_i++;
      if (!(*(cstr_i)))  // if all whitespace, return nothing
         {
         Str.assign("");
         return 0;
         }
      }

   // skip trailing whitespace:
   char* cstr_j = cstr;
   cstr_j += Str.size();
   cstr_j--;
   while ((*(cstr_j) == ' ') || (*(cstr_j) == '\t'))
      { cstr_j--; }
   cstr_j++;
   *(cstr_j) = '\0';

   Str.assign(cstr_i);
   return Str.size();
   }

//##############################################################################

unsigned short CfgReader::GetParmParts(string& StrOut1, string& StrOut2, const string StrIn)
   {
//cout << "\nin CfgReader::GetParmParts()" << endl;
   StrOut1.assign("");
   StrOut2.assign("");
   if (StrIn.size() == 0)
      { return 0; }

//==============================================================================
   //vector<string> VectStr;
   //GetParmArgs(VectStr, StrIn, '=');
//cout << "VectStr.size() = " << VectStr.size() << endl;
   //if (VectStr.size() != 2)
   //   { return 0; }
   //if (VectStr.at(0).size() < 1)
   //   { return 0; }
   //if (VectStr.at(1).size() < 1)
   //   { return 0; }
   //StrOut1.assign(VectStr.at(0));
   //StrOut2.assign(VectStr.at(1));
//==============================================================================

   unsigned short pd = StrIn.find('=');
//cout << "delimiter found at pd = " << pd << endl;

   if (pd == string::npos)
      {
//cout << "delimiter not found" << endl;
      StrOut1.assign(StrIn);
      trimsides(StrOut1);
      return 1;
      }

   StrOut1.assign(StrIn.substr(0, pd));
   trimsides(StrOut1);

   pd++;
   if (pd < StrIn.size())
      {
      StrOut2.assign(StrIn.substr(pd, StrIn.size() - pd));
      trimsides(StrOut2);
      }
//cout << "StrOut1 = |" << StrOut1 << "|" << endl;
//cout << "StrOut2 = |" << StrOut2 << "|" << endl;
   return 2;
   }

//##############################################################################

unsigned short CfgReader::GetParmArgs(vector<string>& VStr, const string StrIn, const char delimiter)
   {
//cout << "\n\n*******************************************************" << endl;
//cout << "parsing with delimiter = \'" << delimiter << "\'" << endl;
   if (!(VStr.empty()))
      { VStr.erase(VStr.begin(),VStr.end()); }
/*
   if ((delimiter != ',') && (delimiter != '-') && (delimiter != ';')
      && (delimiter != ':') && (delimiter != '^') && (delimiter != '~')
      && (delimiter != '*') && (delimiter != '@') && (delimiter != '#')
      && (delimiter != '%') && (delimiter != '&') && (delimiter != '|')
      && (delimiter != '(') && (delimiter != ')') && (delimiter != '[')
      && (delimiter != ']') && (delimiter != '{') && (delimiter != '}')
      && (delimiter != '+') && (delimiter != '=') && (delimiter != '/')
      && (delimiter != '\\') && (delimiter != '<') && (delimiter != '>'))
      { return 0; }
*/
   if (!strchr(",-;:^~*@#%&|()[]{}+=/\\<>", delimiter))
      { return 0; }

   string Str;
   Str.assign(StrIn);

//cout << "Str (before parsing) = |" << Str << "|" << endl;
   if (Str.size() > 0)
      {
//cout << "initializing [inside_field] to false" << endl;
      bool inside_field = false;
      int i = 0;

      while (i < Str.size())
         {
//cout << "Str.at(" << i << ") = \'" << Str.at(i) << "\'" << endl;
//cout << "inside_field = "; if (inside_field) cout << "true"; else cout << "false"; cout << endl;
         if ((Str.at(i) == ' ') || (Str.at(i) == '\t'))
            {
//cout << "removing whitespace" << endl;
            Str.erase(i, 1); }
         else
            {
//cout << "char is not whitespace" << endl;
            if ((Str.at(i) != '\"') && (Str.at(i) != '\''))
               {
//cout << "char is not single or double quote character" << endl;
               if (Str.at(i) != delimiter)
                  {
//cout << "setting [inside_field] to true" << endl;
                  inside_field = true; }
               else
                  {
//cout << "\ndelimiter found" << endl;
//cout << "setting [inside_field] to false\n" << endl;
                  inside_field = false;
                  Str.at(i) = '\x1F';
                  }
               i++;
               }
            else
               {
//cout << "char is a single or double quote character" << endl;
               if (!inside_field)
                  {
                  char qdelim = Str.at(i);
                  Str.erase(i, 1);

                  while ((i < Str.size()) && (Str.at(i) != qdelim))
                     { i++; }

                  if (i == Str.size())
                     { break; }

                  if (Str.at(i) == qdelim)
                     {
                     Str.erase(i, 1);
//cout << "setting [inside_field] to false" << endl;
                     inside_field = false;
                     }
                  }
               else
                  { i++; }
               }  // end of "char is (single or double) quote character"
            }  // end of "char is not whitespace"
         }  // end of "while (i < Str.size())" loop
      }
//cout << " Str (after parsing) = |" << Str << "|" << endl;

   if (Str.size() == 0)
      { return 0; }

   int pos1 = 0;
   int pos2;
   while((pos2 = Str.find("\x1F")) != string::npos)
      {
      Str.at(pos2) = '\0';
      VStr.push_back(Str.substr(pos1, pos2-pos1));
      pos1 = pos2 + 1;
      }
   VStr.push_back(Str.substr(pos1, Str.size()-pos1));

   return VStr.size();
   }

unsigned short CfgReader::GetParmArgs(deque<string>& VStr, const string StrIn, const char delimiter)
   {
   if (!(VStr.empty()))
      { VStr.erase(VStr.begin(),VStr.end()); }

   if (!strchr(",-;:^~*@#%&|()[]{}+=/\\<>", delimiter))
      { return 0; }

   string Str;
   Str.assign(StrIn);

//cout << "Str (before parsing) = |" << Str << "|" << endl;
   if (Str.size() > 0)
      {
      bool inside_field = false;
      int i = 0;

      while (i < Str.size())
         {
         if ((Str.at(i) == ' ') || (Str.at(i) == '\t'))
            { Str.erase(i, 1); }
         else
            {
            if ((Str.at(i) != '\"') && (Str.at(i) != '\''))
               {
               if (Str.at(i) != delimiter)
                  { inside_field = true; }
               else
                  {
                  inside_field = false;
                  Str.at(i) = '\x1F';
                  }
               i++;
               }
            else
               {
//cout << "i = " << i << endl;
//cout << "inside_field = " << inside_field << endl;
               if (!inside_field)
                  {
                  char qdelim = Str.at(i);
                  Str.erase(i, 1);

                  while ((i < Str.size()) && (Str.at(i) != qdelim))
                     { i++; }

                  if (i == Str.size())
                     { break; }

                  if (Str.at(i) == qdelim)
                     {
                     Str.erase(i, 1);
                     inside_field = false;
                     }
                  }
               else
                  { i++; }
               }
            }
         }  // end of "while (i < Str.size())" loop
      }
//cout << " Str (after parsing) = |" << Str << "|" << endl;

   if (Str.size() == 0)
      { return 0; }

   int pos1 = 0;
   int pos2;
   while((pos2 = Str.find("\x1F")) != string::npos)
      {
      Str.at(pos2) = '\0';
      VStr.push_back(Str.substr(pos1, pos2-pos1));
      pos1 = pos2 + 1;
      }
   VStr.push_back(Str.substr(pos1, Str.size()-pos1));

   return VStr.size();
   }

// this is "holdover" code from when I changed the name of the function:
unsigned short CfgReader::GetParmItems(vector<string>& VStr, const string StrIn, const char delimiter)
   { return GetParmArgs(VStr, StrIn, delimiter); }
unsigned short CfgReader::GetParmItems(deque<string>& VStr, const string StrIn, const char delimiter)
   { return GetParmArgs(VStr, StrIn, delimiter); }

//##############################################################################

#endif  // _CfgSTL_h_

 PREVIOUS PAGE   C++ PAGE 
 HOME   CONTACT ME 
This page: Created 10/13/00. Last updated 10/17/00.
Copyright © 2000  Todd S. Greene.