Read a configuration file
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>
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")
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>