www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - [RFC] ini parser

reply bioinfornatics <bioinfornatics fedoraproject.org> writes:
I have wrote a ini parser it can use bidirectional range

example of ini file able to parse:
_______________________________________
[sectionA]
    param1=3Dvalue1
    param2=3Dvalue2
    [[subSectionA]]
        param1sub=3Dvalue1sub
[sectionB]
    param3=3Dvalue3
    ; I am a comment
    param4=3Dvalue4
[sectionC]
    param5=3Dvalue5
    param6=3Dvalue6
_______________________________________


Example to code who use ini parser

_______________________________________
import std.string;
import std.stdio;
import std.ini;

void main( ){
    writeln( "0 - Starting test" );
    IniFile tester =3D open("myConfig.ini");
    writeln( "Ini file loaded" );
    writefln( "1 - Name: %s, level: %d", tester.name, tester.level);
    writefln( "2 - childs:\n%s,", tester.childs );
    writefln( "3 - object is null: %s", tester is null );
    writefln( "4 - number of section: %d", tester.length );
    writefln( "5 - sectionA: %s", tester.get("sectionA") );
    writefln( "6 - sectionA.param1: %s",
tester.get("sectionA")["param1"] );
    writefln( "7 - first section:\n%s", tester.front );
    writeln(  "8 - popfront" );
    tester.popFront();
    writefln( "9 - first section:\n%s", tester.front );

}
----------------OUTPUT------------------------
0 - Starting test
Ini file loaded
1 - Name: root, level: 0
2 - childs:
[[sectionA]
param1=3Dvalue1
param2=3Dvalue2
[[subSectionA]]
param1sub=3Dvalue1sub
, [sectionB]
param3=3Dvalue3
param4=3Dvalue4
, [sectionC]
param5=3Dvalue5
param6=3Dvalue6
],
3 - object is null: false
4 - number of section: 3
5 - sectionA: [sectionA]
param1=3Dvalue1
param2=3Dvalue2
[[subSectionA]]
param1sub=3Dvalue1sub

6 - sectionA.param1: value1
7 - first section:
[sectionA]
param1=3Dvalue1
param2=3Dvalue2
[[subSectionA]]
param1sub=3Dvalue1sub

8 - popfront
9 - first section:
[sectionB]
param3=3Dvalue3
param4=3Dvalue4


_______________________________________



The code
_______________________________________
module std.ini;

private import std.stream       : BufferedFile;
private import std.string       : format, stripLeft, stripRight;
private import std.array        : split;
private import std.stdio        : File;
private import std.stdio        : writeln, writefln, writef;
private import std.exception    : Exception;


/**
 * parse is a method for parse a INI file or config file.
 *
 * Returns: A Section object with all information
 *
 * Examples:
 * --------------------
 * import std.ini;
 * string filePath  =3D "~/myGreatSetup.conf";
 * Section sections =3D configFile.open( filePath );
 * --------------------
 */

IniFile open( string filePath ){
    Section         root            =3D new Section("root",
0);           // root section
    Section         currentSection  =3D
root;                             // reference to current section
    Section         nextSection     =3D null;
    File            iniFile         =3D File( filePath, "r" );
    foreach( line; iniFile.byLine()
){                                  // read line by line
        try{
            line =3D line.stripLeft().stripRight();
            if( line =3D=3D "" || line[0] =3D=3D ';'
){                            // empty line line or comment line
                continue;
            }
            else if( line[0] =3D=3D '['
){                                  // section start
                nextSection =3D getSection( cast(string)line
);           // get newest section
                if( currentSection.level < nextSection.level
){         // currentSection.level < nextSection.level
                    currentSection.addChild( nextSection
);             // add a child to current section
                    currentSection =3D
nextSection;                       // now current section go to next one
                }
                else if( currentSection.level =3D=3D nextSection.level
){   // currentSection.level =3D nextSection.level
                    currentSection =3D
currentSection.rewind( currentSection.parent.level );
                    currentSection.addChild( nextSection );
                    currentSection =3D nextSection;
                }

else{                                                   //
currentSection.level > nextSection.level
                    currentSection =3D
currentSection.rewind( nextSection.level - 1);
                    currentSection.addChild( nextSection );
                    currentSection =3D nextSection;
                }
            }

else{                                                       // read
information corresponding to a section
                string[] words =3D split(cast(string)line,
"=3D");          // get key / value peer
                foreach( ref string word; words )

word.stripRight().stripLeft();                      // remove space,
before and after word
                currentSection[ words[0] ] =3D words[1];
            }
        }
        catch(Exception e){
            writeln( "Error: config file seem to not not follow
specification!" );
            writeln( e.msg );
            writefln( "Line: %s", line );
        }
    }
    root.shrink;
    return root;
}

alias Section IniFile;
class Section{
    private:
        string          _name;
        Section         _parent;
        Section[]       _childs;
        size_t          _level;
        size_t          _numberOfChild;
        string[string]  _dict;

    public:
        /**
         * Constructor for a Section object
         *
         * Params: name level
         */
        this(string name, size_t level){
            this._name           =3D name;
            this._level          =3D level;
            this._childs         =3D [];
            this._numberOfChild  =3D 0;
            this._dict           =3D null;
        }

        /**
         * Constructor for copy Section object
         *
         * Params: name parent level childs numberOfChild dict
         */
        this( string name, Section parent, size_t level, Section[]
childs, size_t numberOfChild, string[string] dict ){
            this._name           =3D name;
            this._level          =3D level;
            this._childs.length  =3D childs.length;
            foreach(size_t index, child; childs)
                this._childs[index] =3D child.dup;
            this._numberOfChild  =3D numberOfChild;
            this._dict           =3D dict;
        }

        /**
         * addChild is used for add a subsection to current section
         *
         * Params: Section
         */
        void addChild( ref Section section ){
            if( _numberOfChild >=3D _childs.length )
                _childs.length      =3D _childs.length + 5;        //
resize +5 for not resize 1 by 1
            section.parent          =3D this;
            _childs[_numberOfChild] =3D section;
            _numberOfChild++;
        }

        /**
         * Resize object to same size as data contained by the object
         */
         property void shrink(){
            _childs.length =3D _numberOfChild;
            foreach( child; _childs )
                child.shrink;
        }

        /**
         * get return the subsection where name equal name given
         *
         * Params: name
         *
         * Retuns: Section, null if not found
         */
        Section get( string name ){
            Section section     =3D null;
            bool    isSearching =3D true;
            size_t  index       =3D 0;
            while( isSearching ){
                if( index >=3D _numberOfChild )
                    isSearching =3D false;
                else if( _childs[index].name =3D=3D name ){
                    isSearching =3D false;
                    section =3D _childs[index].dup;
                }
                index++;
            }
            return section;
        }


        /**
         * opIndex
         * Acces to a value in current Section by giving his key
         */
        string opIndex( string key ){
            return _dict[key];
        }

        /**
         * opIndexAssign
         * Append a pair key/value in current Section
         */
        void opIndexAssign( string  value, string key ){
            _dict[key.idup] =3D value.idup;
        }

        /**
         * rewind is used for come back to parent at level given
         *
         * Params: level
         */
        Section rewind( size_t levelToGo
){                            // rewind to parent level x
            Section section     =3D null;
            if( _level =3D=3D levelToGo)
                section =3D this;
            else if( _level >=3D levelToGo)
                section =3D _parent.rewind( levelToGo );
            else
                throw new Exception("You try to go back when current
section is lower where level you want to go!");
            return section;
        }

        /**
         * toString used for print current object state
         *
         * Returns: a string
         */
        override string toString(){
            string content =3D "";
            string start   =3D "";
            string end     =3D "";
            if( _name !=3D "root" ){
                foreach(i; 0 .. _level){
                    start   ~=3D "[";
                    end     ~=3D "]";
                }
                content ~=3D start ~ _name ~ end ~ "\n"; // [section1] ...
[[section2]]
                foreach( key, value; _dict )
                    content ~=3D "%s=3D%s\n".format( key, value );
            }
            foreach(child; _childs){
                content ~=3D child.toString();
            }
            return content.idup;
        }

         property Section dup(){
            return new Section( this._name, this.parent, this._level,
this._childs, this._numberOfChild, this._dict );
        }

         property string name(){
            return _name.idup;
        }

         property Section parent(){
            return _parent;
        }

         property Section parent(Section section){
            return _parent =3D section;
        }

         property Section[] childs(){
            return _childs.dup;
        }

         property size_t level(){
            return _level;
        }

         property size_t length(){
            return _numberOfChild;
        }

         property string[] keys(){
            return _dict.keys;
        }

         property string[] values(){
            return _dict.values;
        }

         property void rehash(){
            _dict.rehash;
            foreach(child; _childs)
                child.rehash;
        }

         property bool empty(){
            return _numberOfChild =3D=3D 0;
        }

         property Section front(){
            return childs[0];
        }

         property Section back(){
            return  rewind( _parent.level );
        }

        void popFront(){
            _childs =3Dchilds[1..$];
        }

        void popBack(){
            Section[] reversed =3D new Section[]( _level ) ;
            while( _level !=3D 0 ){
                reversed[ _level - 1 ] =3D this.back;
            }
        }

        Section save(){
            return this;
        }

}


/**
 * getSection create a Section line with corresponding line
 *
 * Returns: Section object
 *
 * Examples:
 * --------------------
 * string line      =3D "[default]";
 * Section section  =3D getSection( line );
 * --------------------
 */
Section getSection( string lineSection ){
    size_t  level           =3D 0;
    size_t  position        =3D 0;
    string  name            =3D "";
    // get level
    while( lineSection[level] =3D=3D '[' ){
        level++;
    }
    position =3D level;
    // get section name
    while( lineSection[position] !=3D ']' ){
        name ~=3D lineSection[position];
        position++;
    }
    return new Section(name, level);
}
Feb 22 2012
parent bioinfornatics <bioinfornatics fedoraproject.org> writes:
update (minor)
I have do a pull request here:
https://github.com/D-Programming-Language/phobos/pull/449
Feb 22 2012