February 17, 2012
On Thursday, 16 February 2012 at 22:29:30 UTC, Manu wrote:
> I wonder if there is a problem with a 'standard' ini parser, in that ini
> files are not really very standard.
> I have seen a lot of ini files with scope (also also use this in some of my
> own apps), will I be able to parse these with your parser?
>
> [section]
> {
>   key = value
>   key2 = value
>
>   [subsection]
>   {
>     subkey = value
>   }
> }
>
> ?


I parser won't parse it. To have nested sections, it uses delimeter in section names to keep it compatible with standards. If some parser does not supports nesting it still will, but sections would look like [a.b] etc. This curly braces thing can break some parsers.
February 17, 2012
On Friday, 17 February 2012 at 07:27:52 UTC, Jacob Carlborg wrote:
> On 2012-02-16 21:50, Robik wrote:
>> Greetings.
>>
>> Recently, I've been working on INI parser in D. The main goals were to
>> keep code easy and well documented. Suggestions are really welcome(main
>> reason of this thread) because it needs polishing and stuff.
>>
>> It provides simple interface for operating on parsed file, including
>> useful features like section inheriting and variable lookups. Here is
>> simple example taken from README with little modifications:
>> import std.stdio;
>> void main()
>> {
>> // Hard code the contents
>> string c = "
>> # defaults
>> [def]
>> name1:value1
>> name2:value2
>>
>> ; override defaults

Thanks, it's awesome idea. Added.
>> [foo : def]
>> name1=Name1 from foo. Lookup for def.name2: %name2%";
>>
>> // create parser instance
>> auto iniParser = new IniParser();
>>
>> // Set ini structure details; can be ommited
>> iniParser.commentChars = [';', '#'];
>> iniParser.delimChars = ['=', ':'];
>>
>>
>> // parse
>> auto ini = iniParser.parse(c);
>>
>> // write foo.name1 value
>> writeln(ini.getSection("foo")["name1"].value);
>> }
>
> Why the need for ".value"? It would guess because opIndex returns IniKey which both contains the key and the value. I would sugest you add a "alias this" pointing to "value". Something like this.
>
> struct IniKey
> {
>     string name;
>     string value;
>
>     alias value this;
> }


February 18, 2012
On Thu, 16 Feb 2012 14:50:22 -0600, Robik <szadows@gmail.com> wrote:
> Greetings.
[snip]

>         // write foo.name1 value
>         writeln(ini.getSection("foo")["name1"].value);

I'd recommend using opDispatch to enable the following syntax:

writeln( ini.foo.name1 );

Skimming the code, I see a lot of re-implementation of std.algorithm, to say nothing of the more general issue of using O(N) linear search instead of an O(1) hash table.
February 22, 2012
I have wrote a ini parser it can use bidirectional range

example of ini file able to parse:
_______________________________________
[sectionA]
    param1=value1
    param2=value2
    [[subSectionA]]
        param1sub=value1sub
[sectionB]
    param3=value3
    ; I am a comment
    param4=value4
[sectionC]
    param5=value5
    param6=value6
_______________________________________


Example to code who use ini parser

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

void main( ){
    writeln( "0 - Starting test" );
    IniFile tester = 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 - next section:\n%s", tester.front );
    writefln( "8 - next section:\n%s", tester.front );

}
----------------OUTPUT------------------------
0 - Starting test
Ini file loaded
1 - Name: root, level: 0
2 - childs:
[[sectionA]
param1=value1
param2=value2
[[subSectionA]]
param1sub=value1sub
, [sectionB]
param3=value3
param4=value4
, [sectionC]
param5=value5
param6=value6
],
3 - object is null: false
4 - number of section: 3
5 - sectionA: [sectionA]
param1=value1
param2=value2
[[subSectionA]]
param1sub=value1sub

6 - sectionA.param1: value1
7 - next section:
[sectionA]
param1=value1
param2=value2
[[subSectionA]]
param1sub=value1sub

8 - next section:
[sectionA]
param1=value1
param2=value2
[[subSectionA]]
param1sub=value1sub

_______________________________________



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  = "~/myGreatSetup.conf";
 * Section sections = configFile.open( filePath );
 * --------------------
 */

IniFile open( string filePath ){
    Section         root            = new Section("root",
0);           // root section
    Section         currentSection  =
root;                             // reference to current section
    Section         nextSection     = null;
    File            iniFile         = File( filePath, "r" );
    foreach( line; iniFile.byLine()
){                                  // read line by line
        try{
            line = line.stripLeft().stripRight();
            if( line == "" || line[0] == ';'
){                            // empty line line or comment line
                continue;
            }
            else if( line[0] == '['
){                                  // section start
                nextSection = 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 =
nextSection;                       // now current section go to next one
                }
                else if( currentSection.level == nextSection.level
){   // currentSection.level = nextSection.level
                    currentSection =
currentSection.rewind( currentSection.parent.level );
                    currentSection.addChild( nextSection );
                    currentSection = nextSection;
                }

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

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

word.stripRight().stripLeft();                      // remove space,
before and after word
                currentSection[ words[0] ] = 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           = name;
            this._level          = level;
            this._childs         = [];
            this._numberOfChild  = 0;
            this._dict           = 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           = name;
            this._level          = level;
            this._childs.length  = childs.length;
            foreach(size_t index, child; childs)
                this._childs[index] = child.dup;
            this._numberOfChild  = numberOfChild;
            this._dict           = dict;
        }

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

        /**
         * Resize object to same size as data contained by the object
         */
        @property void shrink(){
            _childs.length = _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     = null;
            bool    isSearching = true;
            size_t  index       = 0;
            while( isSearching ){
                if( index >= _numberOfChild )
                    isSearching = false;
                else if( _childs[index].name == name ){
                    isSearching = false;
                    section = _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] = 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     = null;
            if( _level == levelToGo)
                section = this;
            else if( _level >= levelToGo)
                section = _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 = "";
            string start   = "";
            string end     = "";
            if( _name != "root" ){
                foreach(i; 0 .. _level){
                    start   ~= "[";
                    end     ~= "]";
                }
                content ~= start ~ _name ~ end ~ "\n"; // [section1] ...
[[section2]]
                foreach( key, value; _dict )
                    content ~= "%s=%s\n".format( key, value );
            }
            foreach(child; _childs){
                content ~= 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 = 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 == 0;
        }

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

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

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

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

        Section save(){
            return this;
        }

}


/**
 * getSection create a Section line with corresponding line
 *
 * Returns: Section object
 *
 * Examples:
 * --------------------
 * string line      = "[default]";
 * Section section  = getSection( line );
 * --------------------
 */
Section getSection( string lineSection ){
    size_t  level           = 0;
    size_t  position        = 0;
    string  name            = "";
    // get level
    while( lineSection[level] == '[' ){
        level++;
    }
    position = level;
    // get section name
    while( lineSection[position] != ']' ){
        name ~= lineSection[position];
        position++;
    }
    return new Section(name, level);
}



January 20, 2015
It would be nice to add overload of getKey method so you can use something like this:
this.optionalkey = config.getKey("key1", "defaultValue");

it's need for set default value if needed value can't be found on config.
1 2
Next ›   Last »