Read a configuration file: Difference between revisions

From Rosetta Code
Content added Content deleted
No edit summary
Line 113: Line 113:
needspeeling = 1
needspeeling = 1
seedsremoved = 0 </lang>
seedsremoved = 0 </lang>

=={{header|Perl}}==
<lang perl>
my $fullname;
my $favouritefruit;
my $needspeeling;
my $seedsremoved;
my @otherfamily;

my $conf_definition = {
'fullname' => [ 'string', \$fullname ],
'favouritefruit' => [ 'string', \$favouritefruit ],
'needspeeling' => [ 'boolean', \$needspeeling ],
'seedsremoved' => [ 'boolean', \$seedsremoved ],
'otherfamily' => [ 'array', \@otherfamily ],
};

my $file;
open $file, "file" or die "Can't open configuration file: $!";

read_conf_file($file, $conf_definition);

print "fullname = $fullname\n";
print "favouritefruit = $favouritefruit\n";
print "needspeeling = ", ($needspeeling ? 'true' : 'false'), "\n";
print "seedsremoved = ", ($seedsremoved ? 'true' : 'false'), "\n";
for (my $i = 0; $i < @otherfamily; ++$i) {
print "othernames(", $i + 1, ") = ", $otherfamily[$i], "\n";
}


sub read_conf_file {
my ($fh, $def) = @_;

local $_;
while (<$fh>) {
next if /^#/; # skip "#" comments
next if /^;/; # skip ";" comments
next if /^$/; # skip blank lines
chomp;

$_ =~ /^\s*(\w+)\s*(.*)$/i or die "Syntax error";
my $key = $1;
my $rest = $2;
$key =~ tr/[A-Z]/[a-z]/;

if (!exists $def->{$key}) {
die "Unknown keyword: '$key'";
}

if ($def->{$key}[0] eq 'boolean') {
if ($rest) {
die "Syntax error: extra data following boolean '$key'";
}
${$def->{$key}[1]} = 1;
next;
}

$rest =~ s/\s*$//;
$rest =~ s/^=\s*//;

if ($def->{$key}[0] eq 'string') {
${$def->{$key}[1]} = $rest;
} elsif ($def->{$key}[0] eq 'array') {
@{$def->{$key}[1]} = split /\s*,\s*/, $rest;
} else {
die "Internal error (unknown type in configuration definition)";
}
}
}

</lang>


=={{header|PicoLisp}}==
=={{header|PicoLisp}}==

Revision as of 21:11, 23 June 2011

Task
Read a configuration file
You are encouraged to solve this task according to the task description, using any language you may know.

The task is to read a configuration file in standard configuration file, and set variables accordingly. For this task, we have a configuration file as follows:

# This is a configuration file in standard configuration file format
#
# Lines begininning with a hash or a semicolon are ignored by the application
# program. Blank lines are also ignored by the application program.

# This is the fullname parameter
FULLNAME Foo Barber

# This is a favourite fruit
FAVOURITEFRUIT banana

# This is a boolean that should be set
NEEDSPEELING

# This boolean is commented out
; SEEDSREMOVED

# Configuration option names are not case sensitive, but configuration parameter
# data is case sensitive and may be preserved by the application program.

# An optional equals sign can be used to separate configuration parameter data
# from the option name. This is dropped by the parser. 

# A configuration option may take multiple parameters separated by commas.
# Leading and trailing whitespace around parameter names and parameter data fields
# are ignored by the application program.

OTHERFAMILY Rhu Barber, Harry Barber

For the task we need to set four variables according to the configuration entries as follows:

  • fullname = Foo Barber
  • favouritefruit = banana
  • needspeeling = true
  • seedsremoved = false

We also have an option that contains multiple parameters. These may be stored in an array.

  • othernames(1) = Rhu Barber
  • othernames(2) = Harry Barber

D

<lang d>import std.stdio, std.getopt, std.string, std.conv, std.regexp ;

template VarName(alias Var) { enum VarName = Var.stringof.toupper ; }

void setOpt(alias Var)(const string line) {

   auto m = RegExp(`^`~VarName!Var~`(\s+(.*))?`).match(line) ;
   if(m.length > 0) {
       static if (is(typeof(Var) == string))
           Var = m.length > 2 ? m[2] : "" ;
       static if (is(typeof(Var) == bool))
           Var = true ;
       static if (is(typeof(Var) == int))
           Var = m.length > 2 ? to!int(m[2]) : 0 ;
   }

}

void main(string[] args) {

   string fullname, favouritefruit ;
   bool needspeeling, seedsremoved ; // default false ;
   int count ; // a line of "COUNT 5" added at end of config file
   foreach(line ; File("readcfg.txt").byLine) {
       auto opt = chomp(text(line)) ;
       setOpt!fullname(opt) ;
       setOpt!favouritefruit(opt) ;
       setOpt!needspeeling(opt) ;
       setOpt!seedsremoved(opt) ;
       setOpt!count(opt) ;
   }
   writefln("%14s = %s", VarName!fullname, fullname) ;
   writefln("%14s = %s", VarName!favouritefruit, favouritefruit) ;
   writefln("%14s = %s", VarName!needspeeling, needspeeling) ;
   writefln("%14s = %s", VarName!seedsremoved, seedsremoved) ;
   writefln("%14s = %s", VarName!count, count) ;

}</lang>

J

<lang j>require'regex' set=:4 :'(x)=:y'

cfgString=:4 :0

 y set 
 (1;&,~'(?i:',y,')\s*(.*)') y&set rxapply x

)

cfgBoolean=:4 :0

 y set 0
 (1;&,~'(?i:',y,')\s*(.*)') y&set rxapply x
 if.-.0-:y do.y set 1 end.

)

taskCfg=:3 :0

 cfg=: ('[#;].*';) rxrplc 1!:1<y
 cfg cfgString 'fullname'
 cfg cfgString 'favouritefruit'
 cfg cfgBoolean 'needspeeling'
 cfg cfgBoolean 'seedsremoved'
 i.0 0

)</lang>

Example use:

<lang j> taskCfg 'fruit.conf'

  (,' = ',]&.do)&>;: 'fullname favouritefruit needspeeling seedsremoved'

fullname = Foo Barber favouritefruit = banana needspeeling = 1 seedsremoved = 0 </lang>

Perl

<lang perl> my $fullname; my $favouritefruit; my $needspeeling; my $seedsremoved; my @otherfamily;

my $conf_definition = {

   'fullname'          => [ 'string', \$fullname ],
   'favouritefruit'    => [ 'string', \$favouritefruit ],
   'needspeeling'      => [ 'boolean', \$needspeeling ],
   'seedsremoved'      => [ 'boolean', \$seedsremoved ],
   'otherfamily'       => [ 'array', \@otherfamily ],

};

my $file; open $file, "file" or die "Can't open configuration file: $!";

read_conf_file($file, $conf_definition);

print "fullname = $fullname\n"; print "favouritefruit = $favouritefruit\n"; print "needspeeling = ", ($needspeeling ? 'true' : 'false'), "\n"; print "seedsremoved = ", ($seedsremoved ? 'true' : 'false'), "\n"; for (my $i = 0; $i < @otherfamily; ++$i) {

   print "othernames(", $i + 1, ") = ", $otherfamily[$i], "\n";

}


sub read_conf_file {

   my ($fh, $def) = @_;
   local $_;
   while (<$fh>) {
       next if /^#/;           # skip "#" comments
       next if /^;/;           # skip ";" comments
       next if /^$/;           # skip blank lines
       chomp;
       $_ =~ /^\s*(\w+)\s*(.*)$/i or die "Syntax error";
       my $key = $1;
       my $rest = $2;
       $key =~ tr/[A-Z]/[a-z]/;
       if (!exists $def->{$key}) {
           die "Unknown keyword: '$key'";
       }
       if ($def->{$key}[0] eq 'boolean') {
           if ($rest) {
               die "Syntax error:  extra data following boolean '$key'";
           }
           ${$def->{$key}[1]} = 1;
           next;
       }
       $rest =~ s/\s*$//;
       $rest =~ s/^=\s*//;
       if ($def->{$key}[0] eq 'string') {
           ${$def->{$key}[1]} = $rest;
       } elsif ($def->{$key}[0] eq 'array') {
           @{$def->{$key}[1]} = split /\s*,\s*/, $rest;
       } else {
           die "Internal error (unknown type in configuration definition)";
       }
   }

}

</lang>

PicoLisp

'read' supports only a single comment character. Therefore, we use a pipe to filter the comments. <lang PicoLisp>(de rdConf (File)

  (pipe (in File (while (echo "#" ";") (till "^J")))
     (while (read)
        (set @ (or (line T) T)) ) ) )</lang>

Test: <lang PicoLisp>(off FULLNAME FAVOURITEFRUIT NEEDSPEELING SEEDSREMOVED OTHERFAMILY) (rdConf "conf.txt")</lang> Output:

: (list FULLNAME FAVOURITEFRUIT NEEDSPEELING SEEDSREMOVED OTHERFAMILY)
-> ("Foo Barber" "banana" T NIL "Rhu Barber, Harry Barber")

Python

This task is not well-defined, so we have to make some assumptions (see comments in code). <lang python>def readconf(fn):

   ret = {}
   with file(fn) as fp:
       for line in fp:
           # Assume whitespace is ignorable
           line = line.strip()
           if not line or line.startswith('#'): continue
           
           boolval = True
           # Assume leading ";" means a false boolean
           if line.startswith(';'):
               # Remove one or more leading semicolons
               line = line.lstrip(';')
               # If more than just one word, not a valid boolean
               if len(line.split()) != 1: continue
               boolval = False
           
           bits = line.split(None, 1)
           if len(bits) == 1:
               # Assume booleans are just one standalone word
               k = bits[0]
               v = boolval
           else:
               # Assume more than one word is a string value
               k, v = bits
           ret[k.lower()] = v
   return ret


if __name__ == '__main__':

   import sys
   conf = readconf(sys.argv[1])
   for k, v in sorted(conf.items()):
       print k, '=', v</lang>


Ruby

<lang ruby>fullname = favouritefruit = needspeeling = seedsremoved = false

open("fruit.conf", "r") do |file|

 file.each_line do |line|
   line.chomp!
   key, value = line.split(nil, 2)
   case key
   when /^([#;]|$)/; # ignore line
   when "FULLNAME"; fullname = value
   when "FAVOURITEFRUIT"; favouritefruit = value
   when "NEEDSPEELING"; needspeeling = true
   when "SEEDSREMOVED"; seedsremoved = true
   when /^./; puts "#{key}: unknown key"
   end
 end

end

puts "fullname = #{fullname}" puts "favouritefruit = #{favouritefruit}" puts "needspeeling = #{needspeeling}" puts "seedsremoved = #{seedsremoved}"</lang>

Visual Basic

<lang vb>' Configuration file parser routines. ' ' (c) Copyright 1993 - 2011 Mark Hobley ' ' This configuration parser contains code ported from an application program ' written in Microsoft Quickbasic ' ' This code can be redistributed or modified under the terms of version 1.2 of ' the GNU Free Documentation Licence as published by the Free Software Foundation.

Sub readini()

 var.filename = btrim$(var.winpath) & ini.inifile
 var.filebuffersize = ini.inimaxlinelength
 Call openfileread
 If flg.error = "Y" Then
   flg.abort = "Y"
   Exit Sub
 End If
 If flg.exists <> "Y" Then
   flg.abort = "Y"
   Exit Sub
 End If
 var.inistream = var.stream

readinilabela:

 Call readlinefromfile
 If flg.error = "Y" Then
   flg.abort = "Y"
   Call closestream
   flg.error = "Y"
   Exit Sub
 End If
 If flg.endoffile <> "Y" Then
   iniline$ = message$
   If iniline$ <> "" Then
     If Left$(iniline$, 1) <> ini.commentchar AND Left$(iniline$, 1) <> ini.ignorechar Then
       endofinicommand% = 0
       For l% = 1 To Len(iniline$)
         If Mid$(iniline$, l%, 1) < " " Then
           endofinicommand% = l%
         End If
         If Not (endofinicommand%) Then
           If Mid$(iniline$, l%, 1) = " " Then
             endofinicommand% = l%
           End If
         End If
         If endofinicommand% Then
           l% = Len(iniline$)
         End If
       Next l%
       iniarg$ = ""
       If endofinicommand% Then
         If endofinicommand% <> Len(iniline$) Then
           iniarg$ = btrim$(Mid$(iniline$, endofinicommand% + 1))
           If iniarg$ = "" Then
             GoTo readinilabelb
           End If
           inicommand$ = Left$(iniline$, endofinicommand% - 1)
         End If
       Else
         inicommand$ = btrim$(iniline$)
       End If

readinilabelb:

       'interpret command
       inicommand$ = UCase$(inicommand$)
       Select Case inicommand$
         Case "FULLNAME"
           If iniarg$ <> "" Then
             ini.fullname = iniarg$
           End If
         Case "FAVOURITEFRUIT"
           If iniarg$ <> "" Then
             ini.favouritefruit = iniarg$
           End If
         Case "NEEDSPEELING"
           ini.needspeeling = "Y"
         Case "SEEDSREMOVED"
           ini.seedsremoved = "Y"
         Case "OTHERFAMILY"
           If iniarg$ <> "" Then
             ini.otherfamily = iniarg$
             CALL familyparser
           End If
         Case Else
           '!! error handling required
       End Select
     End If
   End If
   GoTo readinilabela
 End If
 Call closestream
 Exit Sub

readinierror:

End Sub

Sub openfileread()

 flg.streamopen = "N"
 Call checkfileexists
 If flg.error = "Y" Then Exit Sub
 If flg.exists <> "Y" Then Exit Sub
 Call getfreestream
 If flg.error = "Y" Then Exit Sub
 var.errorsection = "Opening File"
 var.errordevice = var.filename
 If ini.errortrap = "Y" Then
   On Local Error GoTo openfilereaderror
 End If
 flg.endoffile = "N"
 Open var.filename For Input As #var.stream Len = var.filebuffersize
 flg.streamopen = "Y"
 Exit Sub

openfilereaderror:

 var.errorcode = Err
 Call errorhandler
 resume '!!

End Sub

Public Sub checkfileexists()

 var.errorsection = "Checking File Exists"
 var.errordevice = var.filename
 If ini.errortrap = "Y" Then
   On Local Error GoTo checkfileexistserror
 End If
 flg.exists = "N"
 If Dir$(var.filename, 0) <> "" Then
   flg.exists = "Y"
 End If
 Exit Sub

checkfileexistserror:

 var.errorcode = Err
 Call errorhandler

End Sub

Public Sub getfreestream()

 var.errorsection = "Opening Free Data Stream"
 var.errordevice = ""
 If ini.errortrap = "Y" Then
   On Local Error GoTo getfreestreamerror
 End If
 var.stream = FreeFile
 Exit Sub

getfreestreamerror:

 var.errorcode = Err
 Call errorhandler
 resume '!!

End Sub

Sub closestream()

 If ini.errortrap = "Y" Then
   On Local Error GoTo closestreamerror
 End If
 var.errorsection = "Closing Stream"
 var.errordevice = ""
 flg.resumenext = "Y"
 Close #var.stream
 If flg.error = "Y" Then
   flg.error = "N"
   '!! Call unexpectederror
 End If
 flg.streamopen = "N"
 Exit Sub

closestreamerror:

 var.errorcode = Err
 Call errorhandler
 resume next

End Sub

Sub readlinefromfile()

 If ini.errortrap = "Y" Then
   On Local Error GoTo readlinefromfileerror
 End If
 If EOF(var.stream) Then
   flg.endoffile = "Y"
   Exit Sub
 End If
 Line Input #var.stream, tmp$
 message$ = tmp$
 Exit Sub

readlinefromfileerror:

 var.errorcode = Err
 Call errorhandler
 resume '!!

End Sub

Public Sub errorhandler()

 tmp$ = btrim$(var.errorsection)
 tmp2$ = btrim$(var.errordevice)
 If tmp2$ <> "" Then
   tmp$ = tmp$ + " (" + tmp2$ + ")"
 End If
 tmp$ = tmp$ + " : " + Str$(var.errorcode)
 tmp1% = MsgBox(tmp$, 0, "Error!")
 flg.error = "Y"
 If flg.resumenext = "Y" Then
   flg.resumenext = "N"

' Resume Next

 Else
   flg.error = "N"

' Resume

 End If

End Sub

Public Function btrim$(arg$)

 btrim$ = LTrim$(RTrim$(arg$))

End Function</lang>