digitalmars.D - [RFC] Ini parser
- "Robik" <szadows gmail.com> Feb 16 2012
- "Nathan M. Swan" <nathanmswan gmail.com> Feb 16 2012
- Manu <turkeyman gmail.com> Feb 16 2012
- Sean Kelly <sean invisibleduck.org> Feb 16 2012
- "Marco Leise" <Marco.Leise gmx.de> Feb 16 2012
- Manu <turkeyman gmail.com> Feb 16 2012
- Manu <turkeyman gmail.com> Feb 16 2012
- "Vladimir Panteleev" <vladimir thecybershadow.net> Feb 16 2012
- Jacob Carlborg <doob me.com> Feb 16 2012
- "Robik" <szadows gmail.com> Feb 17 2012
- "Robik" <szadows gmail.com> Feb 17 2012
- "Robik" <szadows gmail.com> Feb 17 2012
- "Robert Jacques" <sandford jhu.edu> Feb 17 2012
- bioinfornatics <bioinfornatics fedoraproject.org> Feb 21 2012
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
[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);
}
You can also define parsing details, like commentCharacters* and
others. As for the keys, structure is used rather than
associative arrays. There's also bug** that does not allow
chaining with opCall which I hope will be fixed :).
IniStructure (result of parsing) overloads some basic operators
allowing you to looping through it and accessing data with
opIndex and opCall.
Feel free to share suggestions, changes, help me make it better
:).
Repo: https://github.com/robik/DIni
* https://github.com/robik/DIni/blob/master/src/dini.d#L400
** http://d.puremagic.com/issues/show_bug.cgi?id=7210
Feb 16 2012
On Thursday, 16 February 2012 at 20:50:23 UTC, Robik wrote:Feel free to share suggestions, changes, help me make it better :).
It's great, I think I could find uses for it. One thing that confuses me about the implementation is that the IniSection has an array of key-value pairs. I think it would be more efficient, and cleaner code, to use an associative array.
Feb 16 2012
--0016e6d7df59c4850104b91c5b07
Content-Type: text/plain; charset=UTF-8
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 notice your interesting delimiters too, I've never seen anything like
that in an ini file before, where did you see that? What makes it standard?
I might like to use something like that if I had thought it was a normal
thing to use in an ini file...
On 16 February 2012 22:50, Robik <szadows gmail.com> 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
[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);
}
You can also define parsing details, like commentCharacters* and others.
As for the keys, structure is used rather than associative arrays. There's
also bug** that does not allow chaining with opCall which I hope will be
fixed :).
IniStructure (result of parsing) overloads some basic operators allowing
you to looping through it and accessing data with opIndex and opCall.
Feel free to share suggestions, changes, help me make it better :).
Repo: https://github.com/robik/DIni
* https://github.com/robik/DIni/**blob/master/src/dini.d#L400<https://github.com/robik/DIni/blob/master/src/dini.d#L400>
** http://d.puremagic.com/issues/**show_bug.cgi?id=7210<http://d.puremagic.com/issues/show_bug.cgi?id=7210>
--0016e6d7df59c4850104b91c5b07
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
I wonder if there is a problem with a 'standard' ini parser, in tha=
t ini files are not really very standard.<div>I have seen a lot of ini file=
s with scope (also also use this in some of my own apps), will I be able to=
parse these with your parser?</div>
<div><br></div><div>[section]</div><div>{</div><div>=C2=A0 key =3D value</d=
iv><div>=C2=A0 key2 =3D value</div><div><br></div><div>=C2=A0 [subsection]<=
/div><div>=C2=A0 {</div><div>=C2=A0 =C2=A0 subkey =3D value<br></div><div>=
=C2=A0 }</div><div>}<br><br>?</div><div>
<br></div><div>I notice your interesting delimiters too, I've never see=
n anything like that in an ini file before, where did you see that? What ma=
kes it standard?</div><div>I might like to use something like that if I had=
thought it was a normal thing to use in an ini file...</div>
<div><br><div class=3D"gmail_quote">On 16 February 2012 22:50, Robik <span =
dir=3D"ltr"><<a href=3D"mailto:szadows gmail.com">szadows gmail.com</a>&=
gt;</span> wrote:<br><blockquote class=3D"gmail_quote" style=3D"margin:0 0 =
0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Greetings.<br>
<br>
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 rea=
son of this thread) because it needs polishing and stuff.<br>
<br>
It provides simple interface for operating on parsed file, including useful=
features like section inheriting and variable lookups. Here is simple exam=
ple taken from README with little modifications:<br>
import std.stdio;<br>
=C2=A0 void main()<br>
=C2=A0 {<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0// Hard code the contents<br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0string c =3D "<br>
=C2=A0 # defaults<br>
=C2=A0 [def]<br>
=C2=A0 name1:value1<br>
=C2=A0 name2:value2<br>
<br>
=C2=A0 ; override defaults<br>
=C2=A0 [foo : def]<br>
=C2=A0 name1=3DName1 from foo. Lookup for def.name2: %name2%";<br>
<br>
=C2=A0 =C2=A0 =C2=A0 // create parser instance<br>
=C2=A0 =C2=A0 =C2=A0 auto iniParser =3D new IniParser();<br>
<br>
=C2=A0 =C2=A0 =C2=A0 // Set ini structure details; can be ommited<br>
=C2=A0 =C2=A0 =C2=A0 iniParser.commentChars =3D [';', '#']=
;<br>
=C2=A0 =C2=A0 =C2=A0 iniParser.delimChars =3D ['=3D', ':']=
;<br>
<br>
<br>
=C2=A0 =C2=A0 =C2=A0 // parse<br>
=C2=A0 =C2=A0 =C2=A0 auto ini =3D iniParser.parse(c);<br>
<br>
=C2=A0 =C2=A0 =C2=A0 // write foo.name1 value<br>
=C2=A0 =C2=A0 =C2=A0 writeln(ini.getSection("foo")[<u></u>"=
name1"].value);<br>
=C2=A0 }<br>
<br>
You can also define parsing details, like commentCharacters* and others. As=
for the keys, structure is used rather than associative arrays. There'=
s also bug** that does not allow chaining with opCall which I hope will be =
fixed :).<br>
<br>
IniStructure (result of parsing) overloads some basic operators allowing yo=
u to looping through it and accessing data with opIndex and opCall.<br>
<br>
Feel free to share suggestions, changes, help me make it better :).<br>
<br>
Repo: <a href=3D"https://github.com/robik/DIni" target=3D"_blank">https://g=
ithub.com/robik/DIni</a><br>
* <a href=3D"https://github.com/robik/DIni/blob/master/src/dini.d#L400" tar=
get=3D"_blank">https://github.com/robik/DIni/<u></u>blob/master/src/dini.d#=
L400</a><br>
** <a href=3D"http://d.puremagic.com/issues/show_bug.cgi?id=3D7210" target=
=3D"_blank">http://d.puremagic.com/issues/<u></u>show_bug.cgi?id=3D7210</a>=
<br>
</blockquote></div><br></div>
--0016e6d7df59c4850104b91c5b07--
Feb 16 2012
At this point you may as well just use JSON. On Feb 16, 2012, at 2:29 PM, Manu wrote:I wonder if there is a problem with a 'standard' ini parser, in that =
I have seen a lot of ini files with scope (also also use this in some =
=20 [section] { key =3D value key2 =3D value =20 [subsection] { subkey =3D value } } =20 ? =20 I notice your interesting delimiters too, I've never seen anything =
standard?I might like to use something like that if I had thought it was a =
=20 On 16 February 2012 22:50, Robik <szadows gmail.com> wrote: Greetings. =20 Recently, I've been working on INI parser in D. The main goals were to =
reason of this thread) because it needs polishing and stuff.=20 It provides simple interface for operating on parsed file, including =
simple example taken from README with little modifications:import std.stdio; void main() { // Hard code the contents string c =3D " # defaults [def] name1:value1 name2:value2 =20 ; override defaults [foo : def] name1=3DName1 from foo. Lookup for def.name2: %name2%"; =20 // create parser instance auto iniParser =3D new IniParser(); =20 // Set ini structure details; can be ommited iniParser.commentChars =3D [';', '#']; iniParser.delimChars =3D ['=3D', ':']; =20 =20 // parse auto ini =3D iniParser.parse(c); =20 // write foo.name1 value writeln(ini.getSection("foo")["name1"].value); } =20 You can also define parsing details, like commentCharacters* and =
arrays. There's also bug** that does not allow chaining with opCall = which I hope will be fixed :).=20 IniStructure (result of parsing) overloads some basic operators =
opCall.=20 Feel free to share suggestions, changes, help me make it better :). =20 Repo: https://github.com/robik/DIni * https://github.com/robik/DIni/blob/master/src/dini.d#L400 ** http://d.puremagic.com/issues/show_bug.cgi?id=3D7210 =20
Feb 16 2012
Am 16.02.2012, 23:34 Uhr, schrieb Sean Kelly <sean invisibleduck.org>:At this point you may as well just use JSON.
Listen to this guy, he's right. JSON allows hierarchies and arrays, strings, numbers and booleans as values. It is clearly defined and as light-weight as an INI file (compared to XML). I stored game replays in JSON format for http://aichallenge.org/ (gzip compressed and served via HTTP). I found it very flexible for the data structures we came up with and portable since most programming languages have a standard JSON parser.
Feb 16 2012
--00235429c858732c5004b91df708 Content-Type: text/plain; charset=UTF-8 True, but JSON didn't exist/wasn't well known when these formats were in use. On 17 February 2012 00:34, Sean Kelly <sean invisibleduck.org> wrote:At this point you may as well just use JSON. On Feb 16, 2012, at 2:29 PM, Manu wrote:I wonder if there is a problem with a 'standard' ini parser, in that ini
I have seen a lot of ini files with scope (also also use this in some of
[section] { key = value key2 = value [subsection] { subkey = value } } ? I notice your interesting delimiters too, I've never seen anything like
I might like to use something like that if I had thought it was a normal
On 16 February 2012 22:50, Robik <szadows gmail.com> wrote: Greetings. Recently, I've been working on INI parser in D. The main goals were to
reason of this thread) because it needs polishing and stuff.It provides simple interface for operating on parsed file, including
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 [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); } You can also define parsing details, like commentCharacters* and others.
also bug** that does not allow chaining with opCall which I hope will be fixed :).IniStructure (result of parsing) overloads some basic operators allowing
Feel free to share suggestions, changes, help me make it better :). Repo: https://github.com/robik/DIni * https://github.com/robik/DIni/blob/master/src/dini.d#L400 ** http://d.puremagic.com/issues/show_bug.cgi?id=7210
--00235429c858732c5004b91df708 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable True, but JSON didn't exist/wasn't well known when these formats we= re in use.<div><br><div class=3D"gmail_quote">On 17 February 2012 00:34, Se= an Kelly <span dir=3D"ltr"><<a href=3D"mailto:sean invisibleduck.org">se= an invisibleduck.org</a>></span> wrote:<br> <blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:1p= x #ccc solid;padding-left:1ex">At this point you may as well just use JSON.= <br> <div class=3D"HOEnZb"><div class=3D"h5"><br> On Feb 16, 2012, at 2:29 PM, Manu wrote:<br> <br> > I wonder if there is a problem with a 'standard' ini parser, i= n that ini files are not really very standard.<br> > 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?<br> ><br> > [section]<br> > {<br> > =C2=A0 key =3D value<br> > =C2=A0 key2 =3D value<br> ><br> > =C2=A0 [subsection]<br> > =C2=A0 {<br> > =C2=A0 =C2=A0 subkey =3D value<br> > =C2=A0 }<br> > }<br> ><br> > ?<br> ><br> > I notice your interesting delimiters too, I've never seen anything= like that in an ini file before, where did you see that? What makes it sta= ndard?<br> > I might like to use something like that if I had thought it was a norm= al thing to use in an ini file...<br> ><br> > On 16 February 2012 22:50, Robik <<a href=3D"mailto:szadows gmail.c= om">szadows gmail.com</a>> wrote:<br> > Greetings.<br> ><br> > Recently, I've been working on INI parser in D. The main goals wer= e to keep code easy and well documented. Suggestions are really welcome(mai= n reason of this thread) because it needs polishing and stuff.<br> ><br> > It provides simple interface for operating on parsed file, including u= seful features like section inheriting and variable lookups. Here is simple= example taken from README with little modifications:<br> > import std.stdio;<br> > =C2=A0 void main()<br> > =C2=A0 {<br> > =C2=A0 =C2=A0 =C2=A0 =C2=A0// Hard code the contents<br> > =C2=A0 =C2=A0 =C2=A0 =C2=A0string c =3D "<br> > =C2=A0 # defaults<br> > =C2=A0 [def]<br> > =C2=A0 name1:value1<br> > =C2=A0 name2:value2<br> ><br> > =C2=A0 ; override defaults<br> > =C2=A0 [foo : def]<br> > =C2=A0 name1=3DName1 from foo. Lookup for def.name2: %name2%";<br=
> =C2=A0 =C2=A0 =C2=A0 // create parser instance<br> > =C2=A0 =C2=A0 =C2=A0 auto iniParser =3D new IniParser();<br> ><br> > =C2=A0 =C2=A0 =C2=A0 // Set ini structure details; can be ommited<br> > =C2=A0 =C2=A0 =C2=A0 iniParser.commentChars =3D [';', '#&#= 39;];<br> > =C2=A0 =C2=A0 =C2=A0 iniParser.delimChars =3D ['=3D', ':&#= 39;];<br> ><br> ><br> > =C2=A0 =C2=A0 =C2=A0 // parse<br> > =C2=A0 =C2=A0 =C2=A0 auto ini =3D iniParser.parse(c);<br> ><br> > =C2=A0 =C2=A0 =C2=A0 // write foo.name1 value<br> > =C2=A0 =C2=A0 =C2=A0 writeln(ini.getSection("foo")["nam= e1"].value);<br> > =C2=A0 }<br> ><br> > You can also define parsing details, like commentCharacters* and other= s. As for the keys, structure is used rather than associative arrays. There= 's also bug** that does not allow chaining with opCall which I hope wil= l be fixed :).<br> ><br> > IniStructure (result of parsing) overloads some basic operators allowi= ng you to looping through it and accessing data with opIndex and opCall.<br=
> Feel free to share suggestions, changes, help me make it better :).<br=
> Repo: <a href=3D"https://github.com/robik/DIni" target=3D"_blank">http= s://github.com/robik/DIni</a><br> > * <a href=3D"https://github.com/robik/DIni/blob/master/src/dini.d#L400= " target=3D"_blank">https://github.com/robik/DIni/blob/master/src/dini.d#L4= 00</a><br> > ** <a href=3D"http://d.puremagic.com/issues/show_bug.cgi?id=3D7210" ta= rget=3D"_blank">http://d.puremagic.com/issues/show_bug.cgi?id=3D7210</a><br=
<br> </div></div></blockquote></div><br></div> --00235429c858732c5004b91df708--
Feb 16 2012
--00235447104c9c58c404b91dfbe8 Content-Type: text/plain; charset=UTF-8 On 17 February 2012 01:57, Marco Leise <Marco.Leise gmx.de> wrote:Am 16.02.2012, 23:34 Uhr, schrieb Sean Kelly <sean invisibleduck.org>: At this point you may as well just use JSON.
Listen to this guy, he's right. JSON allows hierarchies and arrays, strings, numbers and booleans as values. It is clearly defined and as light-weight as an INI file (compared to XML). I stored game replays in JSON format for http://aichallenge.org/ (gzip compressed and served via HTTP). I found it very flexible for the data structures we came up with and portable since most programming languages have a standard JSON parser.
Sure, I would certainly use JSON now, but we're talking about reading existing data right? Or what's the point of ini at all? --00235447104c9c58c404b91dfbe8 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable <div class=3D"gmail_quote">On 17 February 2012 01:57, Marco Leise <span dir= =3D"ltr"><<a href=3D"mailto:Marco.Leise gmx.de">Marco.Leise gmx.de</a>&g= t;</span> wrote:<br><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0= .8ex;border-left:1px #ccc solid;padding-left:1ex"> Am 16.02.2012, 23:34 Uhr, schrieb Sean Kelly <<a href=3D"mailto:sean inv= isibleduck.org" target=3D"_blank">sean invisibleduck.org</a>>:<div class= =3D"im"><br> <br> <blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:1p= x #ccc solid;padding-left:1ex"> At this point you may as well just use JSON.<br> </blockquote> <br></div> Listen to this guy, he's right. JSON allows hierarchies and arrays, str= ings, numbers and booleans as values. It is clearly defined and as light-we= ight as an INI file (compared to XML). I stored game replays in JSON format= for <a href=3D"http://aichallenge.org/" target=3D"_blank">http://aichallen= ge.org/</a> (gzip compressed and served via HTTP). I found it very flexible= for the data structures we came up with and portable since most programmin= g languages have a standard JSON parser.<br> </blockquote></div><br><div>Sure, I would certainly use JSON now, but we= 9;re talking about reading existing data right? Or what's the point of = ini at all?</div> --00235447104c9c58c404b91dfbe8--
Feb 16 2012
On Thursday, 16 February 2012 at 22:29:30 UTC, Manu wrote: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?
I've never seen such a file. What software (other than yours) uses it?
Feb 16 2012
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 [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; } -- /Jacob Carlborg
Feb 16 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 notice your interesting delimiters too, I've never seen anything like that in an ini file before, where did you see that? What makes it standard? I might like to use something like that if I had thought it was a normal thing to use in an ini file...
Also, you can "make it" simplest/standard-est as possible by disabling those features. To do that you have to set for example 'sectionInheritChar' to 0 to disable it. But to nest sections you have to use something like that: [section] key = value key2 = value [section.subsection] subkey = value Where dot in second section name is delimeter you've set up.
Feb 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.
Feb 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; }
Feb 17 2012
On Thu, 16 Feb 2012 14:50:22 -0600, Robik <szadows gmail.com> wrote:Greetings.
// 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.
Feb 17 2012
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 - 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=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 - next section:
[sectionA]
param1=3Dvalue1
param2=3Dvalue2
[[subSectionA]]
param1sub=3Dvalue1sub
8 - next section:
[sectionA]
param1=3Dvalue1
param2=3Dvalue2
[[subSectionA]]
param1sub=3Dvalue1sub
_______________________________________
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 21 2012









"Nathan M. Swan" <nathanmswan gmail.com> 