LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   config file parsing in C/C++ (https://www.linuxquestions.org/questions/programming-9/config-file-parsing-in-c-c-253597/)

kadissie 11-11-2004 08:45 AM

config file parsing in C/C++
 
I'm looking for a robust, flexible way of reading a configuration file into my C++ program. By "reading a configuration file" I mean taking a file with bash-like variable syntax
Code:

foo=bar
and assigning the value bar to a C++ variable associated with the string "foo".

I know about popt(3) which reads in command line parameters, and this is heart-stoppingly close to what I want - I just don't want to have to code and debug the wrapper that turns bash-like variables into a command-line string. Or write a messy script that turns the variables into a command-line string.

Thanks,
R.

kadissie 11-11-2004 08:47 AM

I'm not fussy about config file formats though - I'd be perfectly happy to use something like the Apache config file, but I must be able to read in all C++ builtin data types.

R.

johnMG 11-11-2004 03:59 PM

Have you tried rolling your own using C++? I'd be curious to see just how messy it comes out to be. I wrote my own for a small program I'm working on, and it's pretty kludgey waiting for me to improve it.

johnMG 11-11-2004 05:33 PM

Ok, well, this is probably way longer than it needs to be, but here's my program's code for parsing its config file. The file consists of lines containing one word, then a number (or three numbers) separated by spaces. Like this:

Code:

light_pos  60.0    0.0  60.0
eye_pos    50.0  10.0  11.0
looking_at  0.0  15.0    0.0
near  20.0
far  80.0
angular_view_width  45.0

Please note that this code is just a first try, and may even be the wrong approach for this. If anyone sees a better way to do this with C++, please let me know.

Code:

void Viewing_geometry::read_in_viewing_params( string file_name )
{
    ifstream config_file( file_name.c_str() );
   
    string line;              // We'll be reading in one line at a time,
    string temp_word;        // examining 1st word in that line.
    string temp_ascii_double; // This is for converting the numerical values to doubles.
   
    vector<double> vec; // We store the parameter's values in this vector.
    double temp_double;
   
    // Possible number separators are space or tab.
    const string delims( " \t" );
   
    // All the possible params that may be in the config.txt file, *in order*.
    vector<string> params;
    params.push_back( "light_pos" );
    params.push_back( "eye_pos" );
    params.push_back( "looking_at" );
    params.push_back( "near" );
    params.push_back( "far" );
    params.push_back( "angular_view_width" );
   
    while ( getline ( config_file, line ) ) // One line, 1 viewing parameter.
    {
        // Be sure to use string::size_type so we can safely compare to string:npos.
        string::size_type beg_idx, end_idx;
       
        beg_idx = line.find_first_not_of( delims ); // Starts at the beginning of the line.
        end_idx = line.find_first_of( delims, beg_idx ); // getting the first word in the line.
        if ( end_idx == string::npos ) // If we're on the last token in a line,
        {                              // set the end_idx appropriately
            end_idx = line.length();
        }
       
        // Get the name of the parameter.
        temp_word = line.substr( beg_idx, end_idx - beg_idx );
       
        // Make sure it's on our list. XXX Maybe change this to use a std algorithm.
        bool temp_word_found = false;
        // Compare temp_word to each word in our list of viable ones.
        for ( vector<string>::const_iterator it = params.begin();
              it != params.end();
              ++it )
        {
            // TODO Add ablility to have comments in the file starting with #.
           
            if ( temp_word == *it )
            {
                temp_word_found = true;
            }
        }
       
        if ( !temp_word_found )
        {
            cout << "WARNING: Couldn't find parameter " << temp_word <<
                    ". Moving on to next line in config.txt." << endl;
            continue;
        }
       
        // Go on to the first number after the descriptive word.
        beg_idx = line.find_first_not_of( delims, end_idx );
       
        // Traverse the rest of line.
        while ( beg_idx != string::npos )
        {
            end_idx = line.find_first_of( delims, beg_idx );
            if ( end_idx == string::npos )
            {
                end_idx = line.length();
            }
           
            // Ok. Now: beg_idx points to the beginning of the substring,
            // and end_idx points to one-past the end of it.
            //temp_ascii_double = string( line, beg_idx, end_idx - beg_idx ); or
            temp_ascii_double = line.substr( beg_idx, end_idx - beg_idx );
            temp_double = strtod( temp_ascii_double.c_str(), NULL );
           
            vec.push_back( temp_double );

            beg_idx = line.find_first_not_of( delims, end_idx );
        }
        // Ok, vec is full. Figure out which param it corresponds to,
        // and copy the values.
               
       
        // I don't yet see a better way to do this... (?) XXX
        if ( temp_word == "light_pos" )
        {
            //cout << "Setting light position: " <<
            //    vec[0] << " " << vec[1] << " " << vec[2] << endl;
            d_light_pos.d_x = vec[0];
            d_light_pos.d_y = vec[1];
            d_light_pos.d_z = vec[2];
        }
                       
        if ( temp_word == "eye_pos" )
        {
            //cout << "Setting eye position: " <<
            //    vec[0] << " " << vec[1] << " " << vec[2] << endl;
            d_eye_pos.d_x = vec[0];
            d_eye_pos.d_y = vec[1];
            d_eye_pos.d_z = vec[2];
        }
                       
        if ( temp_word == "looking_at" )
        {
            //cout << "Setting looking-at position: " <<
            //    vec[0] << " " << vec[1] << " " << vec[2] << endl;
            d_looking_at.d_x = vec[0];
            d_looking_at.d_y = vec[1];
            d_looking_at.d_z = vec[2];
        }
                       
        if ( temp_word == "near" )
        {
            //cout << "Setting near: " << vec[0] << endl;
            d_near = vec[0];
        }
                       
        if ( temp_word == "far" )
        {
            //cout << "Setting far: " << vec[0] << endl;
            d_far = vec[0];
        }
                       
        if ( temp_word == "angular_view_width" )
        {
            //cout << "Setting angular view width (in file): " << vec[0] << endl;
            d_angular_view_width = vec[0];
            // ...but don't forget to convert to radians, which we use
            // exclusively in the code.
            d_angular_view_width = d_angular_view_width * PI / 180.0;
            cout << "and setting d_angular_view_width: " << d_angular_view_width << endl;
        }
                       
        vec.clear();
        // Now on to the next line.
    }
}


ta0kira 11-12-2004 08:49 AM

If you use C++, take advantage of it and avoid the procedural style of code. I ran into the same problem with a command line analyzer (looked a lot like what you have as far as analysis), but later turned it into a parser class. This makes maintenance MUCH easier. Here is how I would go about processing:

1) declare separators (i.e. " " and "\t" general separation, "\n" statement separation, "#" comment, etc.)
2) parse the entire file into strings/statements with minimal whitespace
3) insert them into a list statement objects (a class you make up)
4) (maybe going too far...) create a list of patterns and corresponding function pointers to processing functions
5) register patterns/function pointers in an analyzer class that checks for patterns then dereferences the appropriate function pointer
6) submit the list of statements to the analyzer


This way, everything is generic with the exception of the patterns and the actions. This can be put in a single .cpp file that is nice and neat; only containing pattern strings and global functions for individual actions (all must be the same prototype though). The rest of the implementation is up to you as far as semantics, however it will be highly reusable.

It is interesting you brought that up, because I was thinking about how to implement a config file too. I think the absolute easiest way to do it is to parse and view things as objects as much as possible. I think I'll go home and try to work something out tonight and I'll post the general concept I come up with if you want.
ta0kira

kadissie 11-12-2004 09:08 AM

Dohh! Entering "C++ read configuration file" into google returns some nice results. For my purposes, the best appear to be

http://www-personal.engin.umich.edu/...onfigFile.html
(quite simple; uses templates which may be overkill but is nice if you want to read in user-defined types)

http://spacetown.free.fr/lib/talos/index.php
(a bunch of useful c++-like extensions; includes variable-expansion syntax)

http://www.cours.polymtl.ca/roboop/d...assConfig.html
(non-templated so only works on builtin types, and quite straightforward)

Robert.

johnMG 11-12-2004 12:48 PM

Thanks ta0kira. Yes, I'd like to see what you come up with.

Mara 11-12-2004 03:07 PM

When the configuration file is more complicated you may thing about using flex/bison.

ta0kira 11-15-2004 04:12 AM

johnMG- sorry, it will be a few days. Had some stuff come up.
ta0kira

ta0kira 11-22-2004 05:18 AM

I finally got started on it a few days ago. It looks like it will be too large to post here, but I can post the prototypes and descriptions when I am done.
ta0kira

bcj01 06-30-2009 10:56 AM

Quote:

Originally Posted by ta0kira (Post 1307540)
I finally got started on it a few days ago. It looks like it will be too large to post here, but I can post the prototypes and descriptions when I am done.
ta0kira

Any updates?

tuxdev 06-30-2009 11:51 AM

Look into Boost Program Options. It'll parse command line options and simple key-value config files, and you get a nice --help message for pretty much free.

ta0kira 08-26-2009 05:19 PM

Quote:

Originally Posted by bcj01 (Post 3591652)
Any updates?

Wow, I don't recall exactly the context of this thread (not sure what I was working on 5 years ago); however, I did write a config parser several years ago that I use in a few projects. It works very well for me, although it might not be appropriate for your application. It isn't a stand-alone lib; however, it's contained in 1 header and one source (it would need a few minor changes to be separated from the project.) It's extremely simple in that every line is tokenized based on whitespace, command substitution is allowed, and the rest is up to the program to interpret. I treat the first token as a command and the rest of the line as its options.
http://rservr.berlios.de/user/assembly.html#UCFF
http://rservr.berlios.de/app/client-...on.html#BiT_CP
http://svn.berlios.de/wsvn/rservr/tr...onfig-parser.h
http://svn.berlios.de/wsvn/rservr/tr...fig-parser.cpp

As I said above, this isn't a stand-alone parser at the moment, but it's success as the file parser for two of my largest projects (my actual parser project not included) makes me wonder if it would be appropriate as its own project. What's nice is the actual interpreter can be painfully simple (as it is in its native project,) or it can be very complex, e.g. allowing additional syntax and sematics (as it is in a privately-funded project of mine.)
Kevin Barry


All times are GMT -5. The time now is 05:31 PM.