February 17, 2012 Re: [RFC] Ini parser | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | 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 Re: [RFC] Ini parser | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | 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 Re: [RFC] Ini parser | ||||
---|---|---|---|---|
| ||||
Posted in reply to Robik | 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 Re: [RFC] Ini parser | ||||
---|---|---|---|---|
| ||||
Posted in reply to Robik | 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 Re: [RFC] Ini parser | ||||
---|---|---|---|---|
| ||||
Posted in reply to Robik | 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. |
Copyright © 1999-2021 by the D Language Foundation