Read a configuration file: Difference between revisions

m
(Added (Free) Pascal solution)
m (→‎{{header|Wren}}: Minor tidy)
 
(174 intermediate revisions by 75 users not shown)
Line 1:
{{task|File handling}}
 
The task is to read a configuration file in standard configuration file, and set variables accordingly. For this taskformat, we have a configuration file as follows:
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 begininningbeginning with a hash or a semicolon are ignored by the application
# program. Blank lines are also ignored by the application program.
Line 31 ⟶ 34:
OTHERFAMILY Rhu Barber, Harry Barber
 
 
For the task we need to set four variables according to the configuration entries as follows:
Line 38 ⟶ 42:
*needspeeling = true
*seedsremoved = false
 
 
We also have an option that contains multiple parameters. These may be stored in an array.
Line 44 ⟶ 49:
* otherfamily(2) = Harry Barber
 
=={{header|Ada}}==
 
;Related tasks
{{works with|Ada| 2005}}
* [[Update a configuration file]]
Uses package config_file_parser described in Ada in Denmak Wiki
<br><br>
http://wiki.ada-dk.org/simple_configuration_file_reader_-_using_a_generic
<lang Ada>with Ada.Strings.Unbounded;
with Config_File_Parser;
pragma Elaborate_All (Config_File_Parser);
 
=={{header|11l}}==
package Config is
{{trans|Python}}
 
<syntaxhighlight lang="11l">F readconf(fname)
function TUS (S : String) return Ada.Strings.Unbounded.Unbounded_String
[String = String] ret
renames Ada.Strings.Unbounded.To_Unbounded_String;
L(=line) File(fname).read_lines()
-- Convenience rename. TUS is much shorter than To_Unbounded_String.
line = line.trim((‘ ’, "\t", "\r", "\n"))
I line == ‘’ | line.starts_with(‘#’)
L.continue
 
V boolval = 1B
type Keys is (
I line.starts_with(‘;’)
FULLNAME,
line = line.ltrim(‘;’)
FAVOURITEFRUIT,
NEEDSPEELING,
SEEDSREMOVED,
OTHERFAMILY);
-- These are the valid configuration keys.
 
I line.split_py().len != 1
type Defaults_Array is
L.continue
array (Keys) of Ada.Strings.Unbounded.Unbounded_String;
boolval = 0B
-- The array type we'll use to hold our default configuration settings.
 
V bits = line.split(‘ ’, 2, group_delimiters' 1B)
Defaults_Conf : Defaults_Array :=
String k, v
(FULLNAME => TUS ("John Doe"),
I bits.len == 1
FAVOURITEFRUIT => TUS ("blackberry"),
NEEDSPEELING =>k TUS= ("False"),bits[0]
SEEDSREMOVED =>v TUS= String("False"boolval),
E
OTHERFAMILY => TUS ("Daniel Defoe, Ada Byron"));
(k, v) = bits
-- Default values for the Program object. These can be overwritten by
ret[k.lowercase()] = v
-- the contents of the rosetta.cfg file(see below).
R ret
 
V conf = readconf(‘config.txt’)
package Rosetta_Config is new Config_File_Parser (
L(k, v) sorted(conf.items())
Keys => Keys,
print(k‘ = ’v)</syntaxhighlight>
Defaults_Array => Defaults_Array,
Defaults => Defaults_Conf,
Config_File => "rosetta.cfg");
-- Instantiate the Config configuration object.
 
{{out}}
end Config;</lang>
<pre>
Main program :
favouritefruit = banana
<lang Ada>
fullname = Foo Barber
with Ada.Text_IO;
needspeeling = 1B
with Config; use Config;
otherfamily = Rhu Barber, Harry Barber
seedsremoved = 0B
</pre>
 
=={{header|Ada}}==
procedure Read_Config is
use Ada.Text_IO;
use Rosetta_Config;
 
{{works with|Ada|2005}}
Uses package Config available at SourceForge:
https://sourceforge.net/projects/ini-files/
<syntaxhighlight lang="ada">with Config; use Config;
with Ada.Text_IO; use Ada.Text_IO;
 
procedure Rosetta_Read_Cfg is
cfg: Configuration:= Init("rosetta_read.cfg", Case_Sensitive => False, Variable_Terminator => ' ');
fullname : String := cfg.Value_Of("*", "fullname");
favouritefruit : String := cfg.Value_Of("*", "favouritefruit");
needspeeling : Boolean := cfg.Is_Set("*", "needspeeling");
seedsremoved : Boolean := cfg.Is_Set("*", "seedsremoved");
otherfamily : String := cfg.Value_Of("*", "otherfamily");
begin
Put_Line("fullname = " & fullname);
New_Line;
Put_Line ("Readingfavouritefruit Configuration= File." & favouritefruit);
Put_Line ("Fullnameneedspeeling = " := " & Get Boolean'Image(Key => FULLNAMEneedspeeling));
Put_Line ("Favoriteseedsremoved Fruit := " & Get Boolean'Image(Key => FAVOURITEFRUITseedsremoved));
Put_Line ("Otherotherfamily Family := " & Get (Key =>& OTHERFAMILY)otherfamily);
end;</syntaxhighlight>
if Has_Value (Key => NEEDSPEELING) then
Put_Line ("NEEDSPEELLING := " & Get (Key => NEEDSPEELING));
else
Put_Line ("NEEDSPEELLING := True");
end if;
if Has_Value (Key => SEEDSREMOVED) then
Put_Line ("SEEDSREMOVED := " & Get (Key => SEEDSREMOVED));
else
Put_Line ("SEEDSREMOVED := True");
end if;
end Read_Config;</lang>
 
{{out}}
'''output'''
<pre>
fullname = Foo Barber
Reading Configuration File.
favouritefruit = banana
Fullname := Foo Barber
needspeeling = TRUE
Favorite Fruit := banana
seedsremoved = FALSE
Other Family := Rhu Barber, Harry Barber
otherfamily = Rhu Barber, Harry Barber</pre>
NEEDSPEELLING := True
 
SEEDSREMOVED := False</pre>
=={{header|Aime}}==
<syntaxhighlight lang="aime">record r, s;
integer c;
file f;
list l;
text an, d, k;
 
an = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 
f.affix("tmp/config");
 
while ((c = f.peek) ^ -1) {
integer removed;
 
f.side(" \t\r");
c = f.peek;
removed = c == ';';
if (removed) {
f.pick;
f.side(" \t\r");
c = f.peek;
}
c = place(an, c);
if (-1 < c && c < 52) {
f.near(an, k);
if (removed) {
r[k] = "false";
} else {
f.side(" \t\r");
if (f.peek == '=') {
f.pick;
f.side(" \t\r");
}
f.ever(",#\n", d);
d = bb_drop(d, " \r\t");
if (f.peek != ',') {
r[k] = ~d ? d : "true";
} else {
f.news(l, 0, 0, ",");
lf_push(l, d);
for (c, d in l) {
l[c] = bb_drop(d, " \r\t").bf_drop(" \r\t").string;
}
s.put(k, l);
f.seek(-1, SEEK_CURRENT);
}
}
}
 
f.slip;
}
 
r.wcall(o_, 0, 2, ": ", "\n");
 
for (k, l in s) {
o_(k, ": ");
l.ucall(o_, 0, ", ");
o_("\n");
}</syntaxhighlight>
{{Out}}
<pre>FAVOURITEFRUIT: banana
FULLNAME: Foo Barber
NEEDSPEELING: true
SEEDSREMOVED: false
OTHERFAMILY: Rhu Barber, Harry Barber,</pre>
 
=={{header|Arturo}}==
 
<syntaxhighlight lang="rebol">parseConfig: function [f][
lines: split.lines read f
lines: select map lines 'line [strip replace line {/[#;].*/} ""]
'line [not? empty? line]
result: #[]
 
fields: loop lines 'line [
field: first match line {/^[A-Z]+/}
rest: strip replace line field ""
parts: select map split.by:"," rest => strip 'part -> not? empty? part
 
val: null
case [(size parts)]
when? [= 0] -> val: true
when? [= 1] -> val: first parts
else -> val: parts
 
result\[lower field]: val
]
 
return result
]
 
loop parseConfig relative "config.file" [k,v][
if? block? v -> print [k "->" join.with:", " v]
else -> print [k "->" v]
]</syntaxhighlight>
 
{{out}}
 
<pre>fullname -> Foo Barber
favouritefruit -> banana
needspeeling -> true
otherfamily -> Rhu Barber, Harry Barber</pre>
 
=={{header|AutoHotkey}}==
 
<syntaxhighlight lang="autohotkey">
<lang AutoHotkey>
; Author: AlephX, Aug 18 2011
data = %A_scriptdir%\rosettaconfig.txt
Line 164 ⟶ 269:
}
msgbox, FULLNAME %fullname%`nFAVOURITEFRUIT %FAVOURITEFRUIT%`nNEEDSPEELING %NEEDSPEELING%`nSEEDSREMOVED %SEEDSREMOVED%`nOTHERFAMILY %OTHERFAMILY1% + %OTHERFAMILY2%
</syntaxhighlight>
</lang>
 
=={{header|AWK}}==
<syntaxhighlight lang="awk">
# syntax: GAWK -f READ_A_CONFIGURATION_FILE.AWK
BEGIN {
fullname = favouritefruit = ""
needspeeling = seedsremoved = "false"
fn = "READ_A_CONFIGURATION_FILE.INI"
while (getline rec <fn > 0) {
tmp = tolower(rec)
if (tmp ~ /^ *fullname/) { fullname = extract(rec) }
else if (tmp ~ /^ *favouritefruit/) { favouritefruit = extract(rec) }
else if (tmp ~ /^ *needspeeling/) { needspeeling = "true" }
else if (tmp ~ /^ *seedsremoved/) { seedsremoved = "true" }
else if (tmp ~ /^ *otherfamily/) { split(extract(rec),otherfamily,",") }
}
close(fn)
printf("fullname=%s\n",fullname)
printf("favouritefruit=%s\n",favouritefruit)
printf("needspeeling=%s\n",needspeeling)
printf("seedsremoved=%s\n",seedsremoved)
for (i=1; i<=length(otherfamily); i++) {
sub(/^ +/,"",otherfamily[i]) # remove leading spaces
sub(/ +$/,"",otherfamily[i]) # remove trailing spaces
printf("otherfamily(%d)=%s\n",i,otherfamily[i])
}
exit(0)
}
function extract(rec, pos,str) {
sub(/^ +/,"",rec) # remove leading spaces before parameter name
pos = match(rec,/[= ]/) # determine where data begins
str = substr(rec,pos) # extract the data
gsub(/^[= ]+/,"",str) # remove leading "=" and spaces
sub(/ +$/,"",str) # remove trailing spaces
return(str)
}
</syntaxhighlight>
{{out}}
<pre>
fullname=Foo Barber
favouritefruit=banana
needspeeling=true
seedsremoved=false
otherfamily(1)=Rhu Barber
otherfamily(2)=Harry Barber
</pre>
 
=={{header|BASIC}}==
{{works with|QBasic|1.1}}
{{works with|QuickBasic|4.5}}
{{works with|VB-DOS|1.0}}
{{works with|QB64|1.1}}
 
This is a fully functional program with generic reading of a configuration file with variable names up to 20 characters and values up to 30 characters. Both limits can be expanded as needed but remember how quick it can eat RAM. It can read configuration files with variables separated of values through spaces or equal sign (=), so it will find "Variable = Value" or "Variable Value". Values can be separated by commas in configuration file and it will create a virtual array. This program will omit lines begining with #, ; or null.
<syntaxhighlight lang="qbasic">
' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
' Read a Configuration File V1.0 '
' '
' Developed by A. David Garza Marín in VB-DOS for '
' RosettaCode. December 2, 2016. '
' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
 
OPTION EXPLICIT ' For VB-DOS, PDS 7.1
' OPTION _EXPLICIT ' For QB64
 
' SUBs and FUNCTIONs
DECLARE FUNCTION ErrorMessage$ (WhichError AS INTEGER)
DECLARE FUNCTION YorN$ ()
DECLARE FUNCTION FileExists% (WhichFile AS STRING)
DECLARE FUNCTION ReadConfFile% (NameOfConfFile AS STRING)
DECLARE FUNCTION getVariable$ (WhichVariable AS STRING)
DECLARE FUNCTION getArrayVariable$ (WhichVariable AS STRING, WhichIndex AS INTEGER)
 
' Register for values located
TYPE regVarValue
VarName AS STRING * 20
VarType AS INTEGER ' 1=String, 2=Integer, 3=Real
VarValue AS STRING * 30
END TYPE
 
' Var
DIM rVarValue() AS regVarValue, iErr AS INTEGER, i AS INTEGER, iHMV AS INTEGER
DIM otherfamily(1 TO 2) AS STRING
DIM fullname AS STRING, favouritefruit AS STRING, needspeeling AS INTEGER, seedsremoved AS INTEGER
CONST ConfFileName = "config.fil"
 
' ------------------- Main Program ------------------------
CLS
PRINT "This program reads a configuration file and shows the result."
PRINT
PRINT "Default file name: "; ConfFileName
PRINT
iErr = ReadConfFile(ConfFileName)
IF iErr = 0 THEN
iHMV = UBOUND(rVarValue)
PRINT "Variables found in file:"
FOR i = 1 TO iHMV
PRINT RTRIM$(rVarValue(i).VarName); " = "; RTRIM$(rVarValue(i).VarValue); " (";
SELECT CASE rVarValue(i).VarType
CASE 0: PRINT "Undefined";
CASE 1: PRINT "String";
CASE 2: PRINT "Integer";
CASE 3: PRINT "Real";
END SELECT
PRINT ")"
NEXT i
PRINT
 
' Sets required variables
fullname = getVariable$("FullName")
favouritefruit = getVariable$("FavouriteFruit")
needspeeling = VAL(getVariable$("NeedSpeeling"))
seedsremoved = VAL(getVariable$("SeedsRemoved"))
FOR i = 1 TO 2
otherfamily(i) = getArrayVariable$("OtherFamily", i)
NEXT i
PRINT "Variables requested to set values:"
PRINT "fullname = "; fullname
PRINT "favouritefruit = "; favouritefruit
PRINT "needspeeling = ";
IF needspeeling = 0 THEN PRINT "false" ELSE PRINT "true"
PRINT "seedsremoved = ";
IF seedsremoved = 0 THEN PRINT "false" ELSE PRINT "true"
FOR i = 1 TO 2
PRINT "otherfamily("; i; ") = "; otherfamily(i)
NEXT i
ELSE
PRINT ErrorMessage$(iErr)
END IF
' --------- End of Main Program -----------------------
 
END
 
FileError:
iErr = ERR
RESUME NEXT
 
FUNCTION ErrorMessage$ (WhichError AS INTEGER)
' Var
DIM sError AS STRING
 
SELECT CASE WhichError
CASE 0: sError = "Everything went ok."
CASE 1: sError = "Configuration file doesn't exist."
CASE 2: sError = "There are no variables in the given file."
END SELECT
 
ErrorMessage$ = sError
END FUNCTION
 
FUNCTION FileExists% (WhichFile AS STRING)
' Var
DIM iFile AS INTEGER
DIM iItExists AS INTEGER
SHARED iErr AS INTEGER
 
ON ERROR GOTO FileError
iFile = FREEFILE
iErr = 0
OPEN WhichFile FOR BINARY AS #iFile
IF iErr = 0 THEN
iItExists = LOF(iFile) > 0
CLOSE #iFile
 
IF NOT iItExists THEN
KILL WhichFile
END IF
END IF
ON ERROR GOTO 0
FileExists% = iItExists
 
END FUNCTION
 
FUNCTION getArrayVariable$ (WhichVariable AS STRING, WhichIndex AS INTEGER)
' Var
DIM i AS INTEGER, iHMV AS INTEGER, iCount AS INTEGER
DIM sVar AS STRING, sVal AS STRING, sWV AS STRING
SHARED rVarValue() AS regVarValue
 
' Looks for a variable name and returns its value
iHMV = UBOUND(rVarValue)
sWV = UCASE$(LTRIM$(RTRIM$(WhichVariable)))
sVal = ""
DO
i = i + 1
sVar = UCASE$(RTRIM$(rVarValue(i).VarName))
IF sVar = sWV THEN
iCount = iCount + 1
IF iCount = WhichIndex THEN
sVal = LTRIM$(RTRIM$(rVarValue(i).VarValue))
END IF
END IF
LOOP UNTIL i >= iHMV OR sVal <> ""
 
' Found it or not, it will return the result.
' If the result is "" then it didn't found the requested variable.
getArrayVariable$ = sVal
 
END FUNCTION
 
FUNCTION getVariable$ (WhichVariable AS STRING)
' Var
DIM i AS INTEGER, iHMV AS INTEGER
DIM sVal AS STRING
 
' For a single variable, looks in the first (and only)
' element of the array that contains the name requested.
sVal = getArrayVariable$(WhichVariable, 1)
 
getVariable$ = sVal
END FUNCTION
 
FUNCTION ReadConfFile% (NameOfConfFile AS STRING)
' Var
DIM iFile AS INTEGER, iType AS INTEGER, iVar AS INTEGER, iHMV AS INTEGER
DIM iVal AS INTEGER, iCurVar AS INTEGER, i AS INTEGER, iErr AS INTEGER
DIM dValue AS DOUBLE
DIM sLine AS STRING, sVar AS STRING, sValue AS STRING
SHARED rVarValue() AS regVarValue
 
' This procedure reads a configuration file with variables
' and values separated by the equal sign (=) or a space.
' It needs the FileExists% function.
' Lines begining with # or blank will be ignored.
IF FileExists%(NameOfConfFile) THEN
iFile = FREEFILE
REDIM rVarValue(1 TO 10) AS regVarValue
OPEN NameOfConfFile FOR INPUT AS #iFile
WHILE NOT EOF(iFile)
LINE INPUT #iFile, sLine
sLine = RTRIM$(LTRIM$(sLine))
IF LEN(sLine) > 0 THEN ' Does it have any content?
IF LEFT$(sLine, 1) <> "#" THEN ' Is not a comment?
IF LEFT$(sLine,1) = ";" THEN ' It is a commented variable
sLine = LTRIM$(MID$(sLine, 2))
END IF
iVar = INSTR(sLine, "=") ' Is there an equal sign?
IF iVar = 0 THEN iVar = INSTR(sLine, " ") ' if not then is there a space?
 
GOSUB AddASpaceForAVariable
iCurVar = iHMV
IF iVar > 0 THEN ' Is a variable and a value
rVarValue(iHMV).VarName = LEFT$(sLine, iVar - 1)
ELSE ' Is just a variable name
rVarValue(iHMV).VarName = sLine
rVarValue(iHMV).VarValue = ""
END IF
 
IF iVar > 0 THEN ' Get the value(s)
sLine = LTRIM$(MID$(sLine, iVar + 1))
DO ' Look for commas
iVal = INSTR(sLine, ",")
IF iVal > 0 THEN ' There is a comma
rVarValue(iHMV).VarValue = RTRIM$(LEFT$(sLine, iVal - 1))
GOSUB AddASpaceForAVariable
rVarValue(iHMV).VarName = rVarValue(iHMV - 1).VarName ' Repeats the variable name
sLine = LTRIM$(MID$(sLine, iVal + 1))
END IF
LOOP UNTIL iVal = 0
rVarValue(iHMV).VarValue = sLine
 
' Determine the variable type of each variable found in this step
FOR i = iCurVar TO iHMV
GOSUB DetermineVariableType
NEXT i
END IF
END IF
END IF
WEND
CLOSE iFile
IF iHMV > 0 THEN
REDIM PRESERVE rVarValue(1 TO iHMV) AS regVarValue
iErr = 0 ' Everything ran ok.
ELSE
REDIM rVarValue(1 TO 1) AS regVarValue
iErr = 2 ' No variables found in configuration file
END IF
ELSE
iErr = 1 ' File doesn't exist
END IF
 
ReadConfFile = iErr
 
EXIT FUNCTION
 
AddASpaceForAVariable:
iHMV = iHMV + 1
 
IF UBOUND(rVarValue) < iHMV THEN ' Are there space for a new one?
REDIM PRESERVE rVarValue(1 TO iHMV + 9) AS regVarValue
END IF
RETURN
 
DetermineVariableType:
sValue = RTRIM$(rVarValue(i).VarValue)
IF ASC(LEFT$(sValue, 1)) < 48 OR ASC(LEFT$(sValue, 1)) > 57 THEN
rVarValue(i).VarType = 1 ' String
ELSE
dValue = VAL(sValue)
IF CLNG(dValue) = dValue THEN
rVarValue(i).VarType = 2 ' Integer
ELSE
rVarValue(i).VarType = 3 ' Real
END IF
END IF
RETURN
 
END FUNCTION
</syntaxhighlight>
Run
<pre>
This program reads a configuration file and shows the result.
 
Default file name: config.fil
 
Variables found in file:
FULLNAME = Foo Barber (String)
FAVOURITEFRUIT = banana (String)
NEEDSPEELING = (Undefined)
SEEDSREMOVED = (Undefined)
OTHERFAMILY = Rhu Barber (String)
OTHERFAMILY = Harry Barber (String)
 
Variables requested to set values:
fullname = Foo Barber
favouritefruit = banana
needspeeling = false
seedsremoved = false
otherfamily( 1 ) = Rhu Barber
otherfamily( 2 ) = Harry Barber
</pre>
 
=={{header|BBC BASIC}}==
{{works with|BBC BASIC for Windows}}
<langsyntaxhighlight lang="bbcbasic"> BOOL = 1
NAME = 2
ARRAY = 3
Line 230 ⟶ 664:
WHEN ARRAY: = !^array$()
ENDCASE
= 0</langsyntaxhighlight>
{{out}}
'''Output:'''
<pre>
fullname = Foo Barber
Line 240 ⟶ 674:
otherfamily(2) = Harry Barber
</pre>
 
=={{header|C}}==
 
{{libheader|libconfini}}
'''optimized'''
 
<syntaxhighlight lang="c">#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <confini.h>
 
#define rosetta_uint8_t unsigned char
 
#define FALSE 0
#define TRUE 1
 
#define CONFIGS_TO_READ 5
#define INI_ARRAY_DELIMITER ','
 
/* Assume that the config file represent a struct containing all the parameters to load */
struct configs {
char *fullname;
char *favouritefruit;
rosetta_uint8_t needspeeling;
rosetta_uint8_t seedsremoved;
char **otherfamily;
size_t otherfamily_len;
size_t _configs_left_;
};
 
static char ** make_array (size_t * arrlen, const char * src, const size_t buffsize, IniFormat ini_format) {
/* Allocate a new array of strings and populate it from the stringified source */
*arrlen = ini_array_get_length(src, INI_ARRAY_DELIMITER, ini_format);
char ** const dest = *arrlen ? (char **) malloc(*arrlen * sizeof(char *) + buffsize) : NULL;
if (!dest) { return NULL; }
memcpy(dest + *arrlen, src, buffsize);
char * iter = (char *) (dest + *arrlen);
for (size_t idx = 0; idx < *arrlen; idx++) {
dest[idx] = ini_array_release(&iter, INI_ARRAY_DELIMITER, ini_format);
ini_string_parse(dest[idx], ini_format);
}
return dest;
 
}
 
static int configs_member_handler (IniDispatch *this, void *v_confs) {
 
struct configs *confs = (struct configs *) v_confs;
 
if (this->type != INI_KEY) {
 
return 0;
 
}
 
if (ini_string_match_si("FULLNAME", this->data, this->format)) {
 
if (confs->fullname) { return 0; }
this->v_len = ini_string_parse(this->value, this->format); /* Remove all quotes, if any */
confs->fullname = strndup(this->value, this->v_len);
confs->_configs_left_--;
 
} else if (ini_string_match_si("FAVOURITEFRUIT", this->data, this->format)) {
 
if (confs->favouritefruit) { return 0; }
this->v_len = ini_string_parse(this->value, this->format); /* Remove all quotes, if any */
confs->favouritefruit = strndup(this->value, this->v_len);
confs->_configs_left_--;
 
} else if (ini_string_match_si("NEEDSPEELING", this->data, this->format)) {
 
if (~confs->needspeeling & 0x80) { return 0; }
confs->needspeeling = ini_get_bool(this->value, TRUE);
confs->_configs_left_--;
 
} else if (ini_string_match_si("SEEDSREMOVED", this->data, this->format)) {
 
if (~confs->seedsremoved & 0x80) { return 0; }
confs->seedsremoved = ini_get_bool(this->value, TRUE);
confs->_configs_left_--;
 
} else if (!confs->otherfamily && ini_string_match_si("OTHERFAMILY", this->data, this->format)) {
 
if (confs->otherfamily) { return 0; }
this->v_len = ini_array_collapse(this->value, INI_ARRAY_DELIMITER, this->format); /* Save memory (not strictly needed) */
confs->otherfamily = make_array(&confs->otherfamily_len, this->value, this->v_len + 1, this->format);
confs->_configs_left_--;
 
}
 
/* Optimization: stop reading the INI file when we have all we need */
return !confs->_configs_left_;
 
}
 
static int populate_configs (struct configs * confs) {
 
/* Define the format of the configuration file */
IniFormat config_format = {
.delimiter_symbol = INI_ANY_SPACE,
.case_sensitive = FALSE,
.semicolon_marker = INI_IGNORE,
.hash_marker = INI_IGNORE,
.multiline_nodes = INI_NO_MULTILINE,
.section_paths = INI_NO_SECTIONS,
.no_single_quotes = FALSE,
.no_double_quotes = FALSE,
.no_spaces_in_names = TRUE,
.implicit_is_not_empty = TRUE,
.do_not_collapse_values = FALSE,
.preserve_empty_quotes = FALSE,
.disabled_after_space = TRUE,
.disabled_can_be_implicit = FALSE
};
 
*confs = (struct configs) { NULL, NULL, 0x80, 0x80, NULL, 0, CONFIGS_TO_READ };
 
if (load_ini_path("rosetta.conf", config_format, NULL, configs_member_handler, confs) & CONFINI_ERROR) {
 
fprintf(stderr, "Sorry, something went wrong :-(\n");
return 1;
 
}
 
confs->needspeeling &= 0x7F;
confs->seedsremoved &= 0x7F;
 
return 0;
 
}
 
int main () {
 
struct configs confs;
 
ini_global_set_implicit_value("YES", 0);
 
if (populate_configs(&confs)) {
 
return 1;
 
}
 
/* Print the configurations parsed */
 
printf(
 
"Full name: %s\n"
"Favorite fruit: %s\n"
"Need spelling: %s\n"
"Seeds removed: %s\n",
 
confs.fullname,
confs.favouritefruit,
confs.needspeeling ? "True" : "False",
confs.seedsremoved ? "True" : "False"
 
);
 
for (size_t idx = 0; idx < confs.otherfamily_len; idx++) {
 
printf("Other family[%d]: %s\n", idx, confs.otherfamily[idx]);
 
}
 
/* Free the allocated memory */
 
#define FREE_NON_NULL(PTR) if (PTR) { free(PTR); }
 
FREE_NON_NULL(confs.fullname);
FREE_NON_NULL(confs.favouritefruit);
FREE_NON_NULL(confs.otherfamily);
 
return 0;
 
}</syntaxhighlight>
 
{{out}}
<pre>Full name: Foo Barber
Favorite fruit: banana
Need spelling: True
Seeds removed: False
Other family[0]: Rhu Barber
Other family[1]: Harry Barber</pre>
 
=={{header|C++}}==
Line 247 ⟶ 866:
'''unoptimized'''
 
<langsyntaxhighlight lang="cpp">#include "stdafx.h"
#include <iostream>
#include <fstream>
Line 352 ⟶ 971:
return 0;
}
</syntaxhighlight>
</lang>
 
{{out}}
'''output'''
<pre>Fullname= Foo Barber
Favorite Fruit= banana
Line 362 ⟶ 981:
 
'''Solution without Boost libraries. No optimisation.'''
<langsyntaxhighlight lang="cpp">#include <iostream>
#include <iomanip>
#include <string>
Line 437 ⟶ 1,056:
read_config(inp,outp);
}
</syntaxhighlight>
</lang>
 
{{out}}
'''output'''
<pre>Full Name: Foo Barber
Favourite Fruit: banana
Line 445 ⟶ 1,064:
Seeds removed?: False
Other family members: Rhu Barber, Harry Barber</pre>
 
=={{header|Clojure}}==
<syntaxhighlight lang="clojure">(ns read-conf-file.core
(:require [clojure.java.io :as io]
[clojure.string :as str])
(:gen-class))
 
(def conf-keys ["fullname"
"favouritefruit"
"needspeeling"
"seedsremoved"
"otherfamily"])
 
(defn get-lines
"Read file returning vec of lines."
[file]
(try
(with-open [rdr (io/reader file)]
(into [] (line-seq rdr)))
(catch Exception e (.getMessage e))))
 
(defn parse-line
"Parse passed line returning vec: token, vec of values."
[line]
(if-let [[_ k v] (re-matches #"(?i)^\s*([a-z]+)(?:\s+|=)?(.+)?$" line)]
(let [k (str/lower-case k)]
(if v
[k (str/split v #",\s*")]
[k [true]]))))
 
(defn mk-conf
"Build configuration map from lines."
[lines]
(->> (map parse-line lines)
(filter (comp not nil?))
(reduce (fn
[m [k v]]
(assoc m k v)) {})))
 
(defn output
[conf-keys conf]
(doseq [k conf-keys]
(let [v (get conf k)]
(if v
(println (format "%s = %s" k (str/join ", " v)))
(println (format "%s = %s" k "false"))))))
 
(defn -main
[filename]
(output conf-keys (mk-conf (get-lines filename))))</syntaxhighlight>
 
{{out}}
<pre>
fullname = Foo Barber
favouritefruit = banana
needspeeling = true
seedsremoved = false
otherfamily = Rhu Barber, Harry Barber
</pre>
 
=={{header|COBOL}}==
<syntaxhighlight lang="cobol">
identification division.
program-id. ReadConfiguration.
 
environment division.
configuration section.
repository.
function all intrinsic.
 
input-output section.
file-control.
select config-file assign to "Configuration.txt"
organization line sequential.
data division.
file section.
 
fd config-file.
01 config-record pic is x(128).
 
working-storage section.
77 idx pic 9(3).
77 pos pic 9(3).
77 last-pos pic 9(3).
77 config-key pic x(32).
77 config-value pic x(64).
77 multi-value pic x(64).
77 full-name pic x(64).
77 favourite-fruit pic x(64).
77 other-family pic x(64) occurs 10.
77 need-speeling pic x(5) value "false".
77 seeds-removed pic x(5) value "false".
 
procedure division.
main.
open input config-file
perform until exit
read config-file
at end
exit perform
end-read
move trim(config-record) to config-record
if config-record(1:1) = "#" or ";" or spaces
exit perform cycle
end-if
unstring config-record delimited by spaces into config-key
move trim(config-record(length(trim(config-key)) + 1:)) to config-value
if config-value(1:1) = "="
move trim(config-value(2:)) to config-value
end-if
evaluate upper-case(config-key)
when "FULLNAME"
move config-value to full-name
when "FAVOURITEFRUIT"
move config-value to favourite-fruit
when "NEEDSPEELING"
if config-value = spaces
move "true" to config-value
end-if
if config-value = "true" or "false"
move config-value to need-speeling
end-if
when "SEEDSREMOVED"
if config-value = spaces
move "true" to config-value
end-if,
if config-value = "true" or "false"
move config-value to seeds-removed
end-if
when "OTHERFAMILY"
move 1 to idx, pos
perform until exit
unstring config-value delimited by "," into multi-value with pointer pos
on overflow
move trim(multi-value) to other-family(idx)
move pos to last-pos
not on overflow
if config-value(last-pos:) <> spaces
move trim(config-value(last-pos:)) to other-family(idx)
end-if,
exit perform
end-unstring
add 1 to idx
end-perform
end-evaluate
end-perform
close config-file
 
display "fullname = " full-name
display "favouritefruit = " favourite-fruit
display "needspeeling = " need-speeling
display "seedsremoved = " seeds-removed
perform varying idx from 1 by 1 until idx > 10
if other-family(idx) <> low-values
display "otherfamily(" idx ") = " other-family(idx)
end-if
end-perform
.
</syntaxhighlight>
 
{{out}}
<pre>
fullname = Foo Barber
favouritefruit = banana
needspeeling = true
seedsremoved = false
otherfamily(001) = Rhu Barber
otherfamily(002) = Harry Barber
</pre>
 
=={{header|Common Lisp}}==
Using parser-combinators available in quicklisp:
<syntaxhighlight lang="lisp">(ql:quickload :parser-combinators)
 
(defpackage :read-config
(:use :cl :parser-combinators))
 
(in-package :read-config)
 
(defun trim-space (string)
(string-trim '(#\space #\tab) string))
 
(defun any-but1? (except)
(named-seq? (<- res (many1? (except? (item) except)))
(coerce res 'string)))
 
(defun values? ()
(named-seq? (<- values (sepby? (any-but1? #\,) #\,))
(mapcar 'trim-space values)))
 
(defun key-values? ()
(named-seq? (<- key (word?))
(opt? (many? (whitespace?)))
(opt? #\=)
(<- values (values?))
(cons key (or (if (cdr values) values (car values)) t))))
 
(defun parse-line (line)
(setf line (trim-space line))
(if (or (string= line "") (member (char line 0) '(#\# #\;)))
:comment
(parse-string* (key-values?) line)))
 
(defun parse-config (stream)
(let ((hash (make-hash-table :test 'equal)))
(loop for line = (read-line stream nil nil)
while line
do (let ((parsed (parse-line line)))
(cond ((eq parsed :comment))
((eq parsed nil) (error "config parser error: ~a" line))
(t (setf (gethash (car parsed) hash) (cdr parsed))))))
hash))</syntaxhighlight>
{{out}}
<syntaxhighlight lang="lisp">READ-CONFIG> (with-open-file (s "test.cfg") (parse-config s))
#<HASH-TABLE :TEST EQUAL :COUNT 4 {100BD25B43}>
READ-CONFIG> (maphash (lambda (k v) (print (list k v))) *)
 
("FULLNAME" "Foo Barber")
("FAVOURITEFRUIT" "banana")
("NEEDSPEELING" T)
("OTHERFAMILY" ("Rhu Barber" "Harry Barber"))
NIL
READ-CONFIG> (gethash "SEEDSREMOVED" **)
NIL
NIL
</syntaxhighlight>
 
=={{header|D}}==
<lang d>import std.stdio, std.getopt, std.string, std.conv, std.regexp ;
 
<syntaxhighlight lang="d">import std.stdio, std.string, std.conv, std.regex, std.getopt;
template VarName(alias Var) { enum VarName = Var.stringof.toupper ; }
 
enum VarName(alias var) = var.stringof;
void setOpt(alias Var)(const string line) {
 
auto m = RegExp(`^`~VarName!Var~`(\s+(.*))?`).match(line) ;
void setOpt(alias Var)(in string line) {
if(m.length > 0) {
auto m = match(line, regex(`^(?i)` ~ VarName!Var ~ `(?-i)(\s*=?\s+(.*))?`));
 
if (!m.empty) {
static if (is(typeof(Var) == string[]))
Var = m.captures.length > 2 ? m.captures[2].split(regex(`\s*,\s*`)) : [""];
static if (is(typeof(Var) == string))
Var = m.captures.length > 2 ? m.captures[2] : "" ;
static if (is(typeof(Var) == bool))
Var = true ;
static if (is(typeof(Var) == int))
Var = m.captures.length > 2 ? to!int(m.captures[2]) : 0 ;
}
}
 
void main(in string[] args) {
string fullnamefullName, favouritefruit favouriteFruit;
string[] otherFamily;
bool needspeeling, seedsremoved ; // default false ;
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 optf = chomp(text(line)) "readcfg.conf".File;
 
setOpt!fullname(opt) ;
foreach (line; f.byLine) {
setOpt!favouritefruit(opt) ;
setOpt!needspeeling(auto opt) = line.strip.idup;
 
setOpt!seedsremoved(opt) ;
setOpt!countfullName(opt) ;
setOpt!favouriteFruit(opt);
setOpt!needsPeeling(opt);
setOpt!seedsRemoved(opt);
setOpt!otherFamily(opt);
}
 
writefln("%14s = %s", VarName!fullname, fullname) ;
writefln("%14ss = %s", VarName!favouritefruitfullName, favouritefruitfullName) ;
writefln("%14ss = %s", VarName!needspeelingfavouriteFruit, needspeelingfavouriteFruit) ;
writefln("%14ss = %s", VarName!seedsremovedneedsPeeling, seedsremovedneedsPeeling) ;
writefln("%14ss = %s", VarName!countseedsRemoved, countseedsRemoved) ;
writefln("%s = %s", VarName!otherFamily, otherFamily);
}</lang>
}</syntaxhighlight>
{{out}}
<pre>fullName = Foo Barber
favouriteFruit = banana
needsPeeling = true
seedsRemoved = false
otherFamily = ["Rhu Barber", "Harry Barber", "John"]</pre>
 
=== Variant 2 ===
Correct version with handling optional '=' sign. Config is assembled into one class.
 
<syntaxhighlight lang="d">
import std.stdio, std.string, std.conv, std.regex, std.algorithm;
 
auto reNameValue = ctRegex!(`^(\w+)\s*=?\s*(\S.*)?`);// ctRegex creates regexp parser at compile time
 
// print Config members w/o hardcoding names
void PrintMembers(Config c)
{
foreach(M; __traits(derivedMembers, Config))
writeln(M ~ ` = `, __traits(getMember, c, M));
}
 
void main(in string[] args /* arg[0] is EXE name */) {
 
auto cfg = new Config;
auto f = args[1].File;// open config given in command line
foreach (line; f.byLineCopy.map!(s => s.strip).filter!(s => !s.empty && s[0] != '#' && s[0] != ';')) {// free loop from unnecessary lines
auto m = matchFirst(line, reNameValue);
if (m.empty) { writeln(`Wrong config line: ` ~ line); continue; }
 
switch(m[1].toUpper) {
case `FULLNAME`: cfg.FullName = m[2]; break;
case `FAVOURITEFRUIT`: cfg.FavouriteFruit = m[2]; break;
case `NEEDSPEELING`: cfg.needsPeeling = (m[2].toUpper != `FALSE`); break;
case `SEEDSREMOVED`: cfg.seedsRemoved = (m[2].toUpper != `FALSE`); break;
case `OTHERFAMILY`: cfg.otherFamily = split(m[2], regex(`\s*,\s*`)); break;// regex allows to avoid 'strip' step
default:
writeln(`Unknown config variable: ` ~ m[1]);
}
}
PrintMembers(cfg);
}
 
class Config
{
string FullName;
string FavouriteFruit;
bool needsPeeling;
bool seedsRemoved;
string[] otherFamily;
}
</syntaxhighlight>
{{out}}
On config:
<pre>
# This is a configuration file in standard configuration file format
#
# Lines beginning 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, John
</pre>
Output is:
<pre>
FullName = Foo Barber
FavouriteFruit = banana
needsPeeling = true
seedsRemoved = false
otherFamily = ["Rhu Barber", "Harry Barber", "John"]
</pre>
 
=={{header|DCL}}==
<syntaxhighlight lang="dcl">$ open input config.ini
$ loop:
$ read /end_of_file = done input line
$ line = f$edit( line, "trim" ) ! removes leading and trailing spaces or tabs
$ if f$length( line ) .eq. 0 then $ goto loop
$ first_character = f$extract( 0, 1, line )
$ if first_character .eqs. "#" .or. first_character .eqs. ";" then $ goto loop
$ equal_sign_offset = f$locate( "=", line )
$ length_of_line = f$length( line )
$ if equal_sign_offset .ne. length_of_line then $ line = f$extract( 0, equal_sign_offset, line ) + " " + f$extract( equal_sign_offset + 1, length_of_line, line )
$ option_name = f$element( 0, " ", line )
$ parameter_data = line - option_name - " "
$ if parameter_data .eqs. "" then $ parameter_data = "true"
$ 'option_name = parameter_data
$ show symbol 'option_name
$ goto loop
$ done:
$ close input</syntaxhighlight>
{{out}}
<pre>$ @read_a_configuration_file
FULLNAME = "Foo Barber"
FAVOURITEFRUIT = "banana"
NEEDSPEELING = "true"</pre>
=={{header|Delphi}}==
{{libheader| System.SysUtils}}
{{libheader| uSettings}}
Unit for manager config files, used in [[Update a configuration file]].
<syntaxhighlight lang="delphi">
unit uSettings;
 
interface
 
uses
System.SysUtils, System.IoUtils, System.Generics.Collections, System.Variants;
 
type
TVariable = record
value: variant;
function ToString: string;
class operator Implicit(a: variant): TVariable;
class operator Implicit(a: TVariable): TArray<string>;
class operator Implicit(a: TVariable): string;
end;
 
TSettings = class(TDictionary<string, TVariable>)
private
function GetVariable(key: string): TVariable;
procedure SetVariable(key: string; const Value: TVariable);
function GetKey(line: string; var key: string; var value: variant; var
disable: boolean): boolean;
function GetAllKeys: TList<string>;
public
procedure LoadFromFile(Filename: TfileName);
procedure SaveToFile(Filename: TfileName);
property Variable[key: string]: TVariable read GetVariable write SetVariable; default;
end;
 
implementation
 
{ TVariable }
 
class operator TVariable.Implicit(a: variant): TVariable;
begin
Result.value := a;
end;
 
class operator TVariable.Implicit(a: TVariable): TArray<string>;
begin
if VarIsType(a.value, varArray or varOleStr) then
Result := a.value
else
raise Exception.Create('Error: can''t convert this type data in array');
end;
 
class operator TVariable.Implicit(a: TVariable): string;
begin
Result := a.ToString;
end;
 
function TVariable.ToString: string;
var
arr: TArray<string>;
begin
if VarIsType(value, varArray or varOleStr) then
begin
arr := value;
Result := string.Join(', ', arr).Trim;
end
else
Result := value;
Result := Result.Trim;
end;
 
{ TSettings }
 
function TSettings.GetAllKeys: TList<string>;
var
key: string;
begin
Result := TList<string>.Create;
for key in Keys do
Result.Add(key);
end;
 
function TSettings.GetKey(line: string; var key: string; var value: variant; var
disable: boolean): boolean;
var
line_: string;
j: integer;
begin
line_ := line.Trim;
Result := not (line_.IsEmpty or (line_[1] = '#'));
if not Result then
exit;
 
disable := (line_[1] = ';');
if disable then
delete(line_, 1, 1);
 
var data := line_.Split([' '], TStringSplitOptions.ExcludeEmpty);
case length(data) of
1: //Boolean
begin
key := data[0].ToUpper;
value := True;
end;
 
2: //Single String
begin
key := data[0].ToUpper;
value := data[1].Trim;
end;
 
else // Mult String value or Array of value
begin
key := data[0];
delete(line_, 1, key.Length);
if line_.IndexOf(',') > -1 then
begin
data := line_.Trim.Split([','], TStringSplitOptions.ExcludeEmpty);
for j := 0 to High(data) do
data[j] := data[j].Trim;
value := data;
end
else
value := line_.Trim;
end;
end;
Result := true;
end;
 
function TSettings.GetVariable(key: string): TVariable;
begin
key := key.Trim.ToUpper;
if not ContainsKey(key) then
add(key, false);
 
result := Items[key];
end;
 
procedure TSettings.LoadFromFile(Filename: TfileName);
var
key, line: string;
value: variant;
disabled: boolean;
Lines: TArray<string>;
begin
if not FileExists(Filename) then
exit;
 
Clear;
Lines := TFile.ReadAllLines(Filename);
for line in Lines do
begin
if GetKey(line, key, value, disabled) then
begin
if disabled then
AddOrSetValue(key, False)
else
AddOrSetValue(key, value)
end;
end;
end;
 
procedure TSettings.SaveToFile(Filename: TfileName);
var
key, line: string;
value: variant;
disabled: boolean;
Lines: TArray<string>;
i: Integer;
All_kyes: TList<string>;
begin
All_kyes := GetAllKeys();
SetLength(Lines, 0);
i := 0;
if FileExists(Filename) then
begin
Lines := TFile.ReadAllLines(Filename);
for i := high(Lines) downto 0 do
begin
if GetKey(Lines[i], key, value, disabled) then
begin
if not ContainsKey(key) then
begin
Lines[i] := '; ' + Lines[i];
Continue;
end;
 
All_kyes.Remove(key);
 
disabled := VarIsType(Variable[key].value, varBoolean) and (Variable[key].value
= false);
if not disabled then
begin
if VarIsType(Variable[key].value, varBoolean) then
Lines[i] := key
else
Lines[i] := format('%s %s', [key, Variable[key].ToString])
end
else
Lines[i] := '; ' + key;
end;
end;
 
end;
 
// new keys
i := high(Lines) + 1;
SetLength(Lines, Length(Lines) + All_kyes.Count);
for key in All_kyes do
begin
Lines[i] := format('%s %s', [key, Variable[key].ToString]);
inc(i);
end;
 
Tfile.WriteAllLines(Filename, Lines);
 
All_kyes.Free;
end;
 
procedure TSettings.SetVariable(key: string; const Value: TVariable);
begin
AddOrSetValue(key.Trim.ToUpper, Value);
end;
end.
</syntaxhighlight>
Usage of unit:
<syntaxhighlight lang="delphi">
program ReadAConfigFile;
 
{$APPTYPE CONSOLE}
 
uses
System.SysUtils,
uSettings;
 
const
FileName = 'Config.txt';
 
var
Settings: TSettings;
 
procedure show(key: string; value: string);
begin
writeln(format('%14s = %s', [key, value]));
end;
 
begin
Settings := TSettings.Create;
Settings.LoadFromFile(FileName);
 
for var k in Settings.Keys do
show(k, Settings[k]);
 
Settings.Free;
Readln;
end.</syntaxhighlight>
{{out}}
<pre>FAVOURITEFRUIT = banana
FULLNAME = Foo Barber
NEEDSPEELING = True
SEEDSREMOVED = False
OTHERFAMILY = Rhu Barber, Harry Barber</pre>
 
=={{header|EchoLisp}}==
{{Incorrect|EchoLisp|Makes no attempt to parse the configuration file of the task description.}}
There is no 'config file' in EchoLisp, but a '''(preferences)''' function which is automatically loaded and evaluated at boot-time, and automatically saved after modification. This function can set global parameters, or call other functions, or load libraries.
<syntaxhighlight lang="lisp">
(edit 'preferences)
;; current contents to edit is displayed in the input box
(define (preferences)
(define-syntax-rule (++ n) (begin (set! n (1+ n)) n))
(define-syntax-rule (% a b) (modulo a b))
;; (lib 'gloops)
(lib 'timer))
 
;; enter new preferences
(define (preferences)
(define FULLNAME "Foo Barber")
(define FAVOURITEFRUIT 'banana)
(define NEEDSPELLING #t)
; SEEDSREMOVED
(define OTHERFAMILY '("Rhu Barber" "Harry Barber")))
</syntaxhighlight>
{{out}}
<syntaxhighlight lang="lisp">
;; press F5 or COMMAND-R to reload
EchoLisp - 2.13.12
📗 local-db: db.version: 13
 
;; enter parameters names :
NEEDSPELLING → #t
FAVOURITEFRUIT → banana
SEEDSREMOVED
😡 error: #|user| : unbound variable : SEEDSREMOVED
</syntaxhighlight>
 
=={{header|Elixir}}==
{{trans|Erlang}}
<syntaxhighlight lang="elixir">defmodule Configuration_file do
def read(file) do
File.read!(file)
|> String.split(~r/\n|\r\n|\r/, trim: true)
|> Enum.reject(fn line -> String.starts_with?(line, ["#", ";"]) end)
|> Enum.map(fn line ->
case String.split(line, ~r/\s/, parts: 2) do
[option] -> {to_atom(option), true}
[option, values] -> {to_atom(option), separate(values)}
end
end)
end
def task do
defaults = [fullname: "Kalle", favouritefruit: "apple", needspeeling: false, seedsremoved: false]
options = read("configuration_file") ++ defaults
[:fullname, :favouritefruit, :needspeeling, :seedsremoved, :otherfamily]
|> Enum.each(fn x ->
values = options[x]
if is_boolean(values) or length(values)==1 do
IO.puts "#{x} = #{values}"
else
Enum.with_index(values) |> Enum.each(fn {value,i} ->
IO.puts "#{x}(#{i+1}) = #{value}"
end)
end
end)
end
defp to_atom(option), do: String.downcase(option) |> String.to_atom
defp separate(values), do: String.split(values, ",") |> Enum.map(&String.strip/1)
end
 
Configuration_file.task</syntaxhighlight>
 
{{out}}
<pre>
fullname = Foo Barber
favouritefruit = banana
needspeeling = true
seedsremoved = false
otherfamily(1) = Rhu Barber
otherfamily(2) = Harry Barber
</pre>
 
=={{header|Erlang}}==
 
<syntaxhighlight lang="erlang">
-module( configuration_file ).
 
-export( [read/1, task/0] ).
 
read( File ) ->
{ok, Binary} = file:read_file( File ),
Lines = [X || <<First:8, _T/binary>> = X <- binary:split(Binary, <<"\n">>, [global]), First =/= $#, First =/= $;],
[option_from_binaries(binary:split(X, <<" ">>)) || X <- Lines].
 
task() ->
Defaults = [{fullname, "Kalle"}, {favouritefruit, "apple"}, {needspeeling, false}, {seedsremoved, false}],
Options = read( "configuration_file" ) ++ Defaults,
[io:fwrite("~p = ~p~n", [X, proplists:get_value(X, Options)]) || X <- [fullname, favouritefruit, needspeeling, seedsremoved, otherfamily]].
 
 
 
option_from_binaries( [Option] ) -> {erlang:list_to_atom(string:to_lower(erlang:binary_to_list(Option))), true};
option_from_binaries( [Option, Values] ) -> {erlang:list_to_atom(string:to_lower(erlang:binary_to_list(Option))), option_from_binaries_value(binary:split(Values, <<", ">>))}.
 
option_from_binaries_value( [Value] ) -> erlang:binary_to_list(Value);
option_from_binaries_value( Values ) -> [erlang:binary_to_list(X) || X <- Values].
</syntaxhighlight>
 
{{out}}
<pre>
50> configuration_file:task().
fullname = "Foo Barber"
favouritefruit = "banana"
needspeeling = true
seedsremoved = false
otherfamily = ["Rhu Barber","Harry Barber"]
</pre>
 
=={{header|Fantom}}==
 
<langsyntaxhighlight lang="fantom">
class Main
{
Line 545 ⟶ 1,894:
}
}
</syntaxhighlight>
</lang>
 
=={{header|Forth}}==
Forth is one of the unique languages that provides the programmer with both an extendable interpreter and an extendable compiler. This demonstration starts from the assumption that competent Forth programmers would not write an entire interpreter just to read a config file but would extend the Forth Interpreter to do the job. This approach is more representative of how Forth is used to create small domain specific languages for specific purposes.
 
As a result of taking this approach some liberties have been taken here from the original task. This code creates three operators for the Forth interpreter (SET, RESET, =) and uses these operators to assign values to actual variables in the Forth system. So the "=" sign here is not optional, it is required. The "=" operator parses an entire line of text and assigns it to a string variable. Set and reset assign 0 or -1 (all bits set) to an integer variable. Using the Forth interpreter "as is" also means that all script symbols are separated by a minimum of 1 space character however this is not a great hardship.
 
A more conventional version could of course be created if absolutely mandated however it would not be created in a few lines of code as this one is.
 
Something worth noting is that the FORTH interpreter will halt on a syntax error in the config.txt file. If this was not the proscribed behavior for the application then the FORTH error handler would need modification. This is possible in most systems by using the system error words (abort, catch, throw) appropriately while interpreting the config file.
<syntaxhighlight lang="text">\ declare the configuration variables in the FORTH app
FORTH DEFINITIONS
 
32 CONSTANT $SIZE
 
VARIABLE FULLNAME $SIZE ALLOT
VARIABLE FAVOURITEFRUIT $SIZE ALLOT
VARIABLE NEEDSPEELING
VARIABLE SEEDSREMOVED
VARIABLE OTHERFAMILY(1) $SIZE ALLOT
VARIABLE OTHERFAMILY(2) $SIZE ALLOT
 
: -leading ( addr len -- addr' len' )
begin over c@ bl = while 1 /string repeat ; \ remove leading blanks
 
: trim ( addr len -- addr len) -leading -trailing ; \ remove blanks both ends
 
\ create the config file interpreter -------
VOCABULARY CONFIG \ create a namespace
CONFIG DEFINITIONS \ put things in the namespace
: SET ( addr --) true swap ! ;
: RESET ( addr --) false swap ! ;
: # ( -- ) 1 PARSE 2DROP ; \ parse line and throw away
: = ( addr --) 1 PARSE trim ROT PLACE ; \ string assignment operator
' # alias ; \ 2nd comment operator is simple
 
FORTH DEFINITIONS
\ this command reads and interprets the config.txt file
: CONFIGURE ( -- ) CONFIG s" CONFIG.TXT" INCLUDED FORTH ;
\ config file interpreter ends ------
 
\ tools to validate the CONFIG interpreter
: $. ( str --) count type ;
: BOOL. ( ? --) @ IF ." ON" ELSE ." OFF" THEN ;
 
: .CONFIG CR ." Fullname : " FULLNAME $.
CR ." Favourite fruit: " FAVOURITEFRUIT $.
CR ." Needs peeling : " NEEDSPEELING bool.
CR ." Seeds removed : " SEEDSREMOVED bool.
CR ." Family:"
CR otherfamily(1) $.
CR otherfamily(2) $. ;</syntaxhighlight>
The config file would look like this
<pre>
# READ this file from within FORTH with the command CONFIGURE
FULLNAME = Foo Barber
FAVOURITEFRUIT = banana
 
# This is a boolean that should be set
NEEDSPEELING SET
 
# This boolean is commented out
; SEEDSREMOVED SET
 
OTHERFAMILY(1) = Rhu Barber
OTHERFAMILY(2) = Harry Barber</pre>
Usage and result<pre>
CONFIGURE
Compiling config.txt ok
.config
Fullname : Foo Barber
Favourite fruit: banana
Needs peeling : ON
Seeds removed : OFF
Family:
Rhu Barber
Harry Barber ok</PRE>
 
Note that parsing a config file using the forth text interpreter this way is probably only safe if you are the only one that edits the config file, as it can execute any forth word.
 
=={{header|Fortran}}==
 
<syntaxhighlight lang="fortran">
program readconfig
implicit none
integer, parameter :: strlen = 100
logical :: needspeeling = .false., seedsremoved =.false.
character(len=strlen) :: favouritefruit = "", fullname = "", fst, snd
character(len=strlen), allocatable :: otherfamily(:), tmp(:)
character(len=1000) :: line
integer :: lun, stat, j, j0, j1, ii = 1, z
integer, parameter :: state_begin=1, state_in_fst=2, state_in_sep=3
 
open(newunit=lun, file="config.ini", status="old")
do
read(lun, "(a)", iostat=stat) line
if (stat<0) exit
if ((line(1:1) == "#") .or. &
(line(1:1) == ";") .or. &
(len_trim(line)==0)) then
cycle
end if
z = state_begin
do j = 1, len_trim(line)
if (z == state_begin) then
if (line(j:j)/=" ") then
j0 = j
z = state_in_fst
end if
elseif (z == state_in_fst) then
if (index("= ",line(j:j))>0) then
fst = lower(line(j0:j-1))
z = state_in_sep
end if
elseif (z == state_in_sep) then
if (index(" =",line(j:j)) == 0) then
snd = line(j:)
exit
end if
else
stop "not possible to be here"
end if
end do
if (z == state_in_fst) then
fst = lower(line(j0:))
elseif (z == state_begin) then
cycle
end if
 
if (fst=="fullname") then
read(snd,"(a)") fullname
elseif (fst=="favouritefruit") then
read(snd,"(a)") favouritefruit
elseif (fst=="seedsremoved") then
seedsremoved = .true.
elseif (fst=="needspeeling") then
needspeeling = .true.
elseif (fst=="otherfamily") then
j = 1; ii = 1
do while (len_trim(snd(j:)) >0)
j1 = index(snd(j:),",")
if (j1==0) then
j1 = len_trim(snd)
else
j1 = j + j1 - 2
end if
do
if (j>len_trim(snd)) exit
if (snd(j:j) /= " ") exit
j = j +1
end do
allocate(tmp(ii))
tmp(1:ii-1) = otherfamily
call move_alloc(tmp, otherfamily)
read(snd(j:j1),"(a)"), otherfamily(ii)
j = j1 + 2
ii = ii + 1
end do
else
print *, "unknown option '"//trim(fst)//"'"; stop
end if
end do
close(lun)
 
print "(a,a)","fullname = ", trim(fullname)
print "(a,a)","favouritefruit = ", trim(favouritefruit)
print "(a,l)","needspeeling = ", needspeeling
print "(a,l)","seedsremoved = ", seedsremoved
print "(a,*(a,:,', '))", "otherfamily = ", &
(trim(otherfamily(j)), j=1,size(otherfamily))
 
contains
 
pure function lower (str) result (string)
implicit none
character(*), intent(In) :: str
character(len(str)) :: string
Integer :: ic, i
 
character(26), parameter :: cap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
character(26), parameter :: low = 'abcdefghijklmnopqrstuvwxyz'
 
string = str
do i = 1, len_trim(str)
ic = index(cap, str(i:i))
if (ic > 0) string(i:i) = low(ic:ic)
end do
end function
 
end program
</syntaxhighlight>
 
=={{header|FreeBASIC}}==
<syntaxhighlight lang="freebasic">' FB 1.05.0 Win64
 
Sub split (s As Const String, sepList As Const String, result() As String)
If s = "" OrElse sepList = "" Then
Redim result(0)
result(0) = s
Return
End If
Dim As Integer i, j, count = 0, empty = 0, length
Dim As Integer position(Len(s) + 1)
position(0) = 0
For i = 0 To len(s) - 1
For j = 0 to Len(sepList) - 1
If s[i] = sepList[j] Then
count += 1
position(count) = i + 1
End If
Next j
Next i
Redim result(count)
If count = 0 Then
result(0) = s
Return
End If
position(count + 1) = len(s) + 1
For i = 1 To count + 1
length = position(i) - position(i - 1) - 1
result(i - 1) = Mid(s, position(i - 1) + 1, length)
Next
End Sub
 
Type ConfigData
fullName As String
favouriteFruit As String
needsPeeling As Boolean
seedsRemoved As Boolean
otherFamily(Any) As String
End Type
 
Sub readConfigData(fileName As String, cData As ConfigData)
Dim fileNum As Integer = FreeFile
Open fileName For Input As #fileNum
If err > 0 Then
Print "File could not be opened"
Sleep
End
End If
Dim ln As String
While Not Eof(fileNum)
Line Input #fileNum, ln
If ln = "" OrElse Left(ln, 1) = "#" OrElse Left(ln, 1) = ";" Then Continue While
If UCase(Left(ln, 8)) = "FULLNAME" Then
cData.fullName = Trim(Mid(ln, 9), Any " =")
ElseIf UCase(Left(ln, 14)) = "FAVOURITEFRUIT" Then
cData.favouriteFruit = Trim(Mid(ln, 15), Any " =")
ElseIf UCase(Left(ln, 12)) = "NEEDSPEELING" Then
Dim s As String = Trim(Mid(ln, 13), Any " =")
If s = "" OrElse UCase(s) = "TRUE" Then
cData.needsPeeling = True
Else
cData.needsPeeling = False
End If
ElseIf UCase(Left(ln, 12)) = "SEEDSREMOVED" Then
Dim s As String = Trim(Mid(ln, 13), Any " =")
If s = "" OrElse UCase(s) = "TRUE" Then
cData.seedsRemoved = True
Else
cData.seedsRemoved = False
End If
ElseIf UCase(Left(ln, 11)) = "OTHERFAMILY" Then
split Mid(ln, 12), ",", cData.otherFamily()
For i As Integer = LBound(cData.otherFamily) To UBound(cData.otherFamily)
cData.otherFamily(i) = Trim(cData.otherFamily(i), Any " =")
Next
End If
Wend
Close #fileNum
End Sub
 
Dim fileName As String = "config.txt"
Dim cData As ConfigData
readConfigData fileName, cData
Print "Full name = "; cData.fullName
Print "Favourite fruit = "; cData.favouriteFruit
Print "Needs peeling = "; cData.needsPeeling
Print "Seeds removed = "; cData.seedsRemoved
For i As Integer = LBound(cData.otherFamily) To UBound(cData.otherFamily)
Print "Other family("; Str(i); ") = "; cData.otherFamily(i)
Next
Print
Print "Press any key to quit"
Sleep</syntaxhighlight>
 
{{out}}
<pre>
Full name = Foo Barber
Favourite fruit = banana
Needs peeling = true
Seeds removed = false
Other family(0) = Rhu Barber
Other family(1) = Harry Barber
</pre>
 
=={{header|FutureBasic}}==
<syntaxhighlight lang="futurebasic">
local fn SaveConfiguration
CFDictionaryRef defaults = @{¬
@"FULLNAME" : @"Foo Barber",¬
@"FAVOURITEFRUIT" : @"banana",¬
@"NEEDSPEELING" : @YES,¬
@"SEEDSREMOVED" : @NO,¬
@"OTHERFAMILY" : @[@"Rhu Barber", @"Harry Barber"]}
UserDefaultsRegisterDefaults( defaults )
end fn
 
local fn ReadConfiguration
CFStringRef tempStr
CFStringRef fullname = fn UserDefaultsString( @"FULLNAME" )
CFStringRef favouritefruit = fn UserDefaultsString( @"FAVOURITEFRUIT" )
BOOL needspeeling = fn UserDefaultsBool( @"NEEDSPEELING" )
BOOL seedsremoved = fn UserDefaultsBool( @"SEEDSREMOVED" )
CFArrayRef otherfamily = fn UserDefaultsArray( @"OTHERFAMILY" )
printf @"Saved configuration:\n"
printf @"FULLNAME: %@", fullname
printf @"FAVOURITEFRUIT: %@", favouritefruit
if needspeeling == YES then tempStr = @"TRUE" else tempStr = @"FALSE"
printf @"NEEDSPEELING: %@", tempStr
if seedsremoved == YES then tempStr = @"TRUE" else tempStr = @"FALSE"
printf @"SEEDSREMOVED: %@", @"(undefined)"
printf @"OTHERFAMILY: %@, %@", otherfamily[0], otherfamily[1]
end fn
 
fn SaveConfiguration
fn ReadConfiguration
 
HandleEvents
</syntaxhighlight>
{{output}}
<pre>
Saved configuration:
 
FULLNAME: Foo Barber
FAVOURITEFRUIT: banana
NEEDSPEELING: TRUE
SEEDSREMOVED: (undefined)
OTHERFAMILY: Rhu Barber, Harry Barber
</pre>
 
=={{header|Gambas}}==
<syntaxhighlight lang="gambas">Public Sub Form_Open()
Dim fullname As String = Settings["fullname", "Foo Barber"] 'If fullname is empty then use the default "Foo Barber"
Dim favouritefruit As String = Settings["favouritefruit", "banana"]
Dim needspeeling As String = Settings["needspeling", True]
Dim seedsremoved As String = Settings["seedsremoved", False]
Dim otherfamily As String[] = Settings["otherfamily", ["Rhu Barber", "Harry Barber"]]
 
Print fullname
 
'To save
Settings["fullname"] = "John Smith"
fullname = Settings["fullname"]
 
Print fullname
 
End</syntaxhighlight>
Output:
<pre>
Foo Barber
John Smith
</pre>
 
=={{header|Go}}==
This make assumptions about the way the config file is supposed to be structured similar to the ones made by the Python solution.
<langsyntaxhighlight lang="go">package config
 
import (
Line 708 ⟶ 2,427:
 
return v, nil
}</langsyntaxhighlight>
 
Usage example:
<langsyntaxhighlight lang="go">package main
 
import (
Line 773 ⟶ 2,492:
fmt.Printf("SEEDSREMOVED: %q\n", seedsremoved)
fmt.Printf("OTHERFAMILY: %q\n", otherfamily)
}</langsyntaxhighlight>
 
=={{header|Groovy}}==
<syntaxhighlight lang="groovy">def config = [:]
def loadConfig = { File file ->
String regex = /^(;{0,1})\s*(\S+)\s*(.*)$/
file.eachLine { line ->
(line =~ regex).each { matcher, invert, key, value ->
if (key == '' || key.startsWith("#")) return
parts = value ? value.split(/\s*,\s*/) : (invert ? [false] : [true])
if (parts.size() > 1) {
parts.eachWithIndex{ part, int i -> config["$key(${i + 1})"] = part}
} else {
config[key] = parts[0]
}
}
}
}</syntaxhighlight>
Testing:
<syntaxhighlight lang="groovy">loadConfig new File('config.ini')
config.each { println it }</syntaxhighlight>
{{out}}
<pre>FULLNAME=Foo Barber
FAVOURITEFRUIT=banana
NEEDSPEELING=true
SEEDSREMOVED=false
OTHERFAMILY(1)=Rhu Barber
OTHERFAMILY(2)=Harry Barber</pre>
 
=={{header|Haskell}}==
 
<langsyntaxhighlight lang="haskell">
import Data.Char
import Data.List
Line 811 ⟶ 2,557:
defaultConfig :: Config
defaultConfig = Config "" "" False False []
</syntaxhighlight>
</lang>
 
Or, use Data.Configfile:
 
<langsyntaxhighlight lang="haskell">
import Data.ConfigFile
import Data.Either.Utils
Line 824 ⟶ 2,570:
let username = getSetting cp "username"
password = getSetting cp "password"
</syntaxhighlight>
</lang>
This works with configuration files in standard format, i.e.,
# this is a comment
username = myname
 
=={{header|Icon}} and {{header|Unicon}}==
 
The following works in both languages. However boolean values don't exist in either language.
So a variable whose value in other languages is <i>false</i> simply has no value in Icon and Unicon.
If it contains any value then it can considered as being equivalent to <i>true</i>:
 
<syntaxhighlight lang="unicon">procedure main(A)
ws := ' \t'
vars := table()
every line := !&input do {
line ? {
tab(many(ws))
if any('#;') | pos(0) then next
vars[map(tab(upto(ws)\1|0))] := getValue()
}
}
show(vars)
end
 
procedure getValue()
ws := ' \t'
a := []
while not pos(0) do {
tab(many(ws))
put(a, trim(tab(upto(',')|0)))
move(1)
}
return a
end
 
procedure show(t)
every pair := !sort(t) do {
every (s := pair[1]||" = ") ||:= !pair[2] || ", "
write(s[1:-2])
}
end</syntaxhighlight>
 
Sample run on above input:
 
<pre>
->rcf <rcf.in
favouritefruit = banana
fullname = Foo Barber
needspeeling
otherfamily = Rhu Barber, Harry Barber
->
</pre>
Note that <tt>seedsremoved</tt> doesn't exist.
 
=={{header|J}}==
 
<langsyntaxhighlight lang="j">require'regex'
set=:4 :'(x)=:y'
 
Line 852 ⟶ 2,647:
cfg cfgBoolean 'seedsremoved'
i.0 0
)</langsyntaxhighlight>
 
Example use:
 
<langsyntaxhighlight lang="j"> taskCfg 'fruit.conf'
(,' = ',]&.do)&>;: 'fullname favouritefruit needspeeling seedsremoved'
fullname = Foo Barber
favouritefruit = banana
needspeeling = 1
seedsremoved = 0 </langsyntaxhighlight>
 
=={{header|Java}}==
A more natural way to do this in Java would be Properties.load(InputStream) but the example data is not in the format expected by that method (equals signs are optional).
<syntaxhighlight lang="java">import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class ConfigReader {
private static final Pattern LINE_PATTERN = Pattern.compile( "([^ =]+)[ =]?(.*)" );
private static final Map<String, Object> DEFAULTS = new HashMap<String, Object>() {{
put( "needspeeling", false );
put( "seedsremoved", false );
}};
 
public static void main( final String[] args ) {
System.out.println( parseFile( args[ 0 ] ) );
}
 
public static Map<String, Object> parseFile( final String fileName ) {
final Map<String, Object> result = new HashMap<String, Object>( DEFAULTS );
/*v*/ BufferedReader reader = null;
 
try {
reader = new BufferedReader( new FileReader( fileName ) );
for ( String line; null != ( line = reader.readLine() ); ) {
parseLine( line, result );
}
} catch ( final IOException x ) {
throw new RuntimeException( "Oops: " + x, x );
} finally {
if ( null != reader ) try {
reader.close();
} catch ( final IOException x2 ) {
System.err.println( "Could not close " + fileName + " - " + x2 );
}
}
 
return result;
}
 
private static void parseLine( final String line, final Map<String, Object> map ) {
if ( "".equals( line.trim() ) || line.startsWith( "#" ) || line.startsWith( ";" ) )
return;
 
final Matcher matcher = LINE_PATTERN.matcher( line );
 
if ( ! matcher.matches() ) {
System.err.println( "Bad config line: " + line );
return;
}
 
final String key = matcher.group( 1 ).trim().toLowerCase();
final String value = matcher.group( 2 ).trim();
 
if ( "".equals( value ) ) {
map.put( key, true );
} else if ( -1 == value.indexOf( ',' ) ) {
map.put( key, value );
} else {
final String[] values = value.split( "," );
 
for ( int i = 0; i < values.length; i++ ) {
values[ i ] = values[ i ].trim();
}
map.put( key, Arrays.asList( values ) );
}
}
}</syntaxhighlight>
 
{{out}}
<pre>{otherfamily=[Rhu Barber, Harry Barber], favouritefruit=banana, seedsremoved=false, needspeeling=true, fullname=Foo Barber}</pre>
 
A more functional and concise approach using Java 8:
 
<syntaxhighlight lang="java">import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
 
public class ConfigReader {
private static final Pattern LINE_PATTERN = Pattern.compile("([^ =]+)[ =]?(.*)");
 
public static void main(final String[] args) throws IOException {
System.out.println(parseFile(args[0]));
}
 
public static Map<String, Object> parseFile(final String fileName) throws IOException {
final Map<String, Object> result = new HashMap<>();
result.put("needspeeling", false);
result.put("seedsremoved", false);
try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
result.putAll(reader.lines()
.filter(line -> !"".equals(line.trim()) && !line.startsWith("#") && !line.startsWith(";"))
.map(LINE_PATTERN::matcher)
.filter(Matcher::matches)
.collect(Collectors.toMap(matcher -> matcher.group(1).trim().toLowerCase(), matcher -> {
final String value = matcher.group(2).trim();
if ("".equals(value)) {
return true;
} else if (-1 == value.indexOf(',')) {
return value;
}
return Arrays.asList(value.split(",")).stream().map(String::trim).collect(Collectors.toList());
}))
);
}
 
return result;
}
}</syntaxhighlight>
 
{{out}}
<pre>{seedsremoved=false, otherfamily=[Rhu Barber, Harry Barber], needspeeling=true, fullname=Foo Barber, favouritefruit=banana}</pre>
 
=={{header|JavaScript}}==
In JavaScript using an object makes more sense than local variables. This function takes our config file in plain text as the parameter.
 
<syntaxhighlight lang="javascript">function parseConfig(config) {
// this expression matches a line starting with an all capital word,
// and anything after it
var regex = /^([A-Z]+)(.*)$/mg;
var configObject = {};
// loop until regex.exec returns null
var match;
while (match = regex.exec(config)) {
// values will typically be an array with one element
// unless we want an array
// match[0] is the whole match, match[1] is the first group (all caps word),
// and match[2] is the second (everything through the end of line)
var key = match[1], values = match[2].split(",");
if (values.length === 1) {
configObject[key] = values[0];
}
else {
configObject[key] = values.map(function(value){
return value.trim();
});
}
}
return configObject;
} </syntaxhighlight>
 
The result is an object, which can be represented with this JSON.
 
<syntaxhighlight lang="javascript">{
"FULLNAME": " Foo Barber",
"FAVOURITEFRUIT": " banana",
"NEEDSPEELING": "",
"OTHERFAMILY": [
"Rhu Barber",
"Harry Barber"
]
}
</syntaxhighlight>
 
=={{header|jq}}==
{{works with|jq|1.5}}
 
In the following, in the case of collisions, the last-most specification prevails.
<syntaxhighlight lang="jq">def parse:
 
def uc: .name | ascii_upcase;
def parse_boolean:
capture( "(?<name>^[^ ] *$)" )
| { (uc) : true };
 
def parse_var_value:
capture( "(?<name>^[^ ]+)[ =] *(?<value>[^,]+ *$)" )
| { (uc) : .value };
 
def parse_var_array:
capture( "(?<name>^[^ ]+)[ =] *(?<value>.*)" )
| { (uc) : (.value | sub(" +$";"") | [splits(", *")]) };
 
reduce inputs as $i ({};
if $i|length == 0 or test("^[#;]") then .
else . + ($i | ( parse_boolean // parse_var_value // parse_var_array // {} ))
end);
 
parse</syntaxhighlight>
 
'''Invocation'''
 
$ jq -n -R -f parse.jq config.txt
 
{{out}}
<pre>{
"FULLNAME": "Foo Barber",
"FAVOURITEFRUIT": "banana",
"NEEDSPEELING": true,
"OTHERFAMILY": [
"Rhu Barber",
"Harry Barber"
]
}</pre>
 
=={{header|Julia}}==
{{works with|Julia|0.6}}
 
<syntaxhighlight lang="julia">function readconf(file)
vars = Dict()
for line in eachline(file)
line = strip(line)
if !isempty(line) && !startswith(line, '#') && !startswith(line, ';')
fspace = searchindex(line, " ")
if fspace == 0
vars[Symbol(lowercase(line))] = true
else
vname, line = Symbol(lowercase(line[1:fspace-1])), line[fspace+1:end]
value = ',' ∈ line ? strip.(split(line, ',')) : line
vars[vname] = value
end
end
end
for (vname, value) in vars
eval(:($vname = $value))
end
return vars
end
 
readconf("test.conf")
 
@show fullname favouritefruit needspeeling otherfamily</syntaxhighlight>
 
{{out}}
<pre>fullname = "Foo Barber"
favouritefruit = "banana"
needspeeling = true
otherfamily = SubString{String}["Rhu Barber", "Harry Barber"]</pre>
 
=={{header|Kotlin}}==
 
{{works with|Kotlin|1.0.6}}
This example is more verbose than it has to be because of increased effort in providing immutability to the configuration class.
<syntaxhighlight lang="scala">import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Paths
 
data class Configuration(val map: Map<String, Any?>) {
val fullName: String by map
val favoriteFruit: String by map
val needsPeeling: Boolean by map
val otherFamily: List<String> by map
}
 
fun main(args: Array<String>) {
val lines = Files.readAllLines(Paths.get("src/configuration.txt"), StandardCharsets.UTF_8)
val keyValuePairs = lines.map{ it.trim() }
.filterNot { it.isEmpty() }
.filterNot(::commentedOut)
.map(::toKeyValuePair)
 
val configurationMap = hashMapOf<String, Any>("needsPeeling" to false)
for (pair in keyValuePairs) {
val (key, value) = pair
when (key) {
"FULLNAME" -> configurationMap.put("fullName", value)
"FAVOURITEFRUIT" -> configurationMap.put("favoriteFruit", value)
"NEEDSPEELING" -> configurationMap.put("needsPeeling", true)
"OTHERFAMILY" -> configurationMap.put("otherFamily", value.split(" , ").map { it.trim() })
else -> println("Encountered unexpected key $key=$value")
}
}
println(Configuration(configurationMap))
}
 
private fun commentedOut(line: String) = line.startsWith("#") || line.startsWith(";")
 
private fun toKeyValuePair(line: String) = line.split(Regex(" "), 2).let {
Pair(it[0], if (it.size == 1) "" else it[1])
}</syntaxhighlight>
 
=={{header|Ksh}}==
<syntaxhighlight lang="ksh">
#!/bin/ksh
 
# Read a configuration file
 
# # Variables:
#
 
# # The configuration file (below) could be read in from a file
# But this method keeps everything together.
# e.g. config=$(< /path/to/config_file)
 
integer config_num=0
config='# This is a configuration file in standard configuration file format
#
# Lines beginning 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'
 
isComment='#|;'
paraDelim=' |='
boolean="SEEDSREMOVED|NEEDSPEELING"
 
typeset -T Config_t=(
typeset -h 'Full name' fullname
typeset -h 'Favorite fruit' favouritefruit
typeset -h 'Boolean NEEDSPEELING' needspeeling=false
typeset -h 'Boolean SEEDSREMOVED' seedsremoved=false
typeset -a -h 'Other family' otherfamily
 
function set_name {
typeset fn ; fn=$(echo $1) # Strip any leading/trailing white space
_.fullname="${fn}"
}
 
function set_fruit {
typeset fruit ; fruit=$(echo $1)
_.favouritefruit="${fruit}"
}
 
function set_bool {
typeset bool ; typeset -u bool=$1
 
case ${bool} in
NEEDSPEELING) _.needspeeling=true ;;
SEEDSREMOVED) _.seedsremoved=true ;;
esac
}
 
function set_family {
typeset ofam ; ofam=$(echo $1)
typeset farr i ; typeset -a farr ; integer i
 
oldIFS="$IFS" ; IFS=',' ; farr=( ${ofam} ) ; IFS="${oldIFS}"
for ((i=0; i<${#farr[*]}; i++)); do
_.otherfamily[i]=$(echo ${farr[i]})
done
}
)
 
# # Functions:
#
 
# # Function _parseconf(config) - Parse uncommented lines
#
function _parseconf {
typeset _cfg ; _cfg="$1"
typeset _conf ; nameref _conf="$2"
 
echo "${_cfg}" | \
while read; do
[[ $REPLY == @(${isComment})* ]] || [[ $REPLY == '' ]] && continue
_parseline "$REPLY" _conf
done
}
 
function _parseline {
typeset _line ; _line=$(echo $1)
typeset _conf ; nameref _conf="$2"
typeset _param _value ; typeset -u _param
 
_param=${_line%%+(${paraDelim})*}
_value=${_line#*+(${paraDelim})}
 
if [[ ${_param} == @(${boolean}) ]]; then
_conf.set_bool ${_param}
else
case ${_param} in
FULLNAME) _conf.set_name "${_value}" ;;
FAVOURITEFRUIT) _conf.set_fruit ${_value} ;;
OTHERFAMILY) _conf.set_family "${_value}" ;;
esac
fi
}
######
# main #
######
 
typeset -a configuration # Indexed array of configurations
Config_t configuration[config_num]
_parseconf "${config}" configuration[config_num]
 
for cnum in ${!configuration[*]}; do
printf "fullname = %s\n" "${configuration[cnum].fullname}"
printf "favouritefruit = %s\n" ${configuration[cnum].favouritefruit}
printf "needspeeling = %s\n" ${configuration[cnum].needspeeling}
printf "seedsremoved = %s\n" ${configuration[cnum].seedsremoved}
for ((i=0; i<${#configuration[cnum].otherfamily[*]}; i++)); do
print "otherfamily($((i+1))) = ${configuration[cnum].otherfamily[i]}"
done
done
</syntaxhighlight>
{{out}}<pre>
fullname = Foo Barber
favouritefruit = banana
needspeeling = true
seedsremoved = false
otherfamily(1) = Rhu Barber
otherfamily(2) = Harry Barber
</pre>
 
=={{header|Lasso}}==
<syntaxhighlight lang="lasso">local(config = '# This is a configuration file in standard configuration file format
#
# Lines beginning 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
')
// if config is in a file collect it like this
//local(config = file('path/and/file.name') -> readstring)
 
define getconfig(term::string, config::string) => {
 
local(
regexp = regexp(-find = `(?m)^` + #term + `($|\s*=\s*|\s+)(.*)$`, -input = #config, -ignorecase),
result
)
 
while(#regexp -> find) => {
#result = (#regexp -> groupcount > 1 ? (#regexp -> matchString(2) -> trim& || true))
if(#result -> asstring >> ',') => {
#result = #result -> split(',')
#result -> foreach => {#1 -> trim}
}
return #result
}
return false
 
}
 
local(
fullname = getconfig('FULLNAME', #config),
favorite = getconfig('FAVOURITEFRUIT', #config),
sedsremoved = getconfig('SEEDSREMOVED', #config),
needspeel = getconfig('NEEDSPEELING', #config),
otherfamily = getconfig('OTHERFAMILY', #config)
)
 
#fullname
'<br />'
#favorite
'<br />'
#sedsremoved
'<br />'
#needspeel
'<br />'
#otherfamily
'<br />'</syntaxhighlight>
{{out}}
<pre>Foo Barber
banana
false
true
array(Rhu Barber, Harry Barber)</pre>
 
=={{header|Liberty BASIC}}==
<syntaxhighlight lang="lb">
<lang lb>
dim confKeys$(100)
dim confValues$(100)
Line 934 ⟶ 3,236:
next i
end function
</syntaxhighlight>
</lang>
 
Output:
 
{{out}}
<pre>Full name: Foo Barber
likes: banana
Line 948 ⟶ 3,249:
 
=={{header|Lua}}==
<langsyntaxhighlight lang="lua">conf = {}
 
fp = io.open( "conf.txt", "r" )
Line 987 ⟶ 3,288:
print( "", entry )
end
end</langsyntaxhighlight>
 
=={{header|M2000 Interpreter}}==
The congiguration.txt is in a zip file in Encode64 Binary part
We can export to disk or use it as is, through the buffer
 
to make it I use this:
<syntaxhighlight lang="m2000 interpreter">
Declare zip compressor
a$=str$(a$)
Method zip, "AddFromMemory",a$, "configuration.txt" as ok
Method zip,"CreateZipBuffer" as buf1
clipboard String$(eval$(buf1) as Encode64)
 
//where a$ defined (before)
a$={text here from first line
until last line
}
</syntaxhighlight>
 
 
To do Declare Zip Nothing is optional (for User forms isn't though)
 
 
<syntaxhighlight lang="m2000 interpreter">
module check(a$, id as list){
Document Export$
nl$={
}
dim L$() : L$()=piece$(a$,nl$)
if len(L$())=0 then exit
for i=0 to len(L$())-1
a$=trim$(L$(i))
b$=left$(a$, 1)
select case b$
case ";"
examineValue(true)
case >"#"
examineValue(false)
end select
next
Report Export$ // result or Clipboard Export$
Sub examineValue(NotUsed as boolean)
local i
if NotUsed then
a$=trim$(mid$(a$,2))+" "
b$=leftpart$(a$," ")
else
a$+=" "
b$=leftpart$(a$," ")
end if
a$=trim$(rightpart$(a$," "))
// optional = removed
if left$(a$,1)="=" then a$=trim$(mid$(a$,2))
// if not exist ignore it
if exist(id,ucase$(b$)) then
if len(a$)=0 then // we have a boolean
Export$=b$+" = "+if$(NotUsed->"false", "true")+nl$
else.if instr(a$,",")>0 then // multiple value
local a$()
a$()=piece$(a$,",")
for i=0 to len(a$())-1
Export$=format$("{0}({1}) = {2}",b$,i+1, trim$(a$(i)))+nl$
next
else
Export$=b$+" = "+a$+nl$
end if
end if
End Sub
}
valid=list:="FULLNAME", "FAVOURITEFRUIT", "NEEDSPEELING", "SEEDSREMOVED", "OTHERFAMILY"
binary{
UEsDBBQAAAgIAO8FflU2rdfqSAIAANQDAAARAAAAY29uZmlndXJhdGlvbi50eHRT
VgjJyCxWyCxWSFRIzs9Ly0wvLUosyczPU0jLzElVyMxTKC5JzEtJLErBJp2WX5Sb
WMLLpczLpazgk5mXWqyQlJqemZeXmZeuUJ5ZkqGQqJCRWJyhkF+kkKhQnJqbmZyf
k5+nkFiUqpCZnpdflJqikFSpUJKRqpBYUJCTmQw2G2RYQVF+elFirp6CU05iXrZC
DthskLbEnOJ8PHrhGnm5QMbAPAdSlVaak5OXmJuqUJBYlJibWpJaxMvlFurj4+fo
66rglp+v4JRYlAQSRNaYqJCWWJZfWpRZkqqQVlSaWcLL5eYY5h8a5Bni6hYU6hmi
kJSYl5iXiK4rKT8/JzUxT6EkI7FEoTgjvzQnRSEpVaE4tYSXy8/V1SU4wNXVx9PP
HUkfTEtmsUJyfm5ual5JaopCfmkJL5e1QjBIS5Crr3+YqwtEizNKbOQXgCmQ9yDB
lJdfopCcWAyyMa84sySzLFVHIam0BC0SkUJCWSElsSQRbDmKNoXEvBSF3MRKkOsL
ilKLU4vKiAl4R5ibEnMUUgtLE3OKFYoz0/MUkhPzQCaVFqemKJTkKxSngpxQkorL
XWBHgcxLK8rPBVuJ5FM9eHinFOUXFCCcVZBYVJxapKcAdQqa4VATQH4qScxOVcgt
zSnJLMhBShfFcHeBjQTFRmKxHjiNpyamgNI2KFBKihIzc8AJPSOzJLW4IDE5VSGx
KL80LwXJ/dAYQREDB3RaZmpOSjHITPyZASVc/UM8XIPcHH09fSIVgjJKoSlWR8Ej
saioEp5+AVBLAQItABQAAAgIAO8FflU2rdfqSAIAANQDAAARAAAAAAAAAAAAIAAA
AAAAAABjb25maWd1cmF0aW9uLnR4dFBLBQYAAAAAAQABAD8AAAB3AgAAAAA=} as zip1
Declare zip compressor
method zip,"OpenZipBuf", zip1
method zip, "ExtractOneToBuffer", "configuration.txt" as buf
If true then
// save buf to file, the load to document as ANSI 1033
open "configuration.txt" for output as #f
put #f,buf, 1
close #f
document b$ : Load.doc b$, "configuration.txt", 1033
check b$, valid
else
check chr$(eval$(buf)), valid
end if
</syntaxhighlight>
{{out}}
<pre>FULLNAME = Foo Barber
FAVOURITEFRUIT = banana
NEEDSPEELING = true
SEEDSREMOVED = false
OTHERFAMILY(1) = Rhu Barber
OTHERFAMILY(2) = Harry Barber
</pre>
 
=={{header|Mathematica}}/{{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">ClearAll[CreateVar, ImportConfig];
CreateVar[x_, y_String: "True"] := Module[{},
If[StringFreeQ[y, ","]
,
ToExpression[x <> "=" <> y]
,
ToExpression[x <> "={" <> StringJoin@Riffle[StringSplit[y, ","], ","] <> "}"]
]
]
ImportConfig[configfile_String] := Module[{data},
(*data = ImportString[configfile, "List", "Numeric" -> False];*)
data=Import[configfile,"List","Numeric"\[Rule]False];
 
data = StringTrim /@ data;
data = Select[data, # =!= "" &];
data = Select[data, ! StringMatchQ[#, "#" | ";" ~~ ___] &];
data = If[! StringFreeQ[#, " "], StringSplit[#, " ", 2], {#}] & /@ data;
 
CreateVar @@@ data;
]
ImportConfig[file]</syntaxhighlight>
 
=={{header|MATLAB}} / {{header|Octave}}==
Line 993 ⟶ 3,425:
This is defined as a function, parameters are returned as part of a struct. When the first line, and the assignment to return values are removed, it is a script that stores the parameters in the local workspace.
 
<langsyntaxhighlight MATLABlang="matlab">function R = readconf(configfile)
% READCONF reads configuration file.
%
Line 1,031 ⟶ 3,463:
fclose(fid);
whos, % shows the parameter in the local workspace
</langsyntaxhighlight>
 
<pre>R=readconf('file.conf')
Line 1,066 ⟶ 3,498:
}
</pre>
 
=={{header|Nanoquery}}==
<syntaxhighlight lang="nanoquery">import Nanoquery.IO
import dict
 
def get_config(fname)
f = new(File).open(fname)
lines = split(f.readAll(), "\n")
 
values = new(Dict)
for line in lines
line = trim(line)
if len(line) > 0
if not (line .startswith. "#") or (line .startswith. ";")
tokens = split(line, " ")
 
if len(tokens) = 1
values.add(upper(tokens[0]), true)
else
parameters = list()
parameter = ""
for i in range(1, len(tokens) - 1)
parameter += tokens[i] + " "
if parameter .endswith. ", "
parameter = parameter.substring(0, len(parameter) - 2)
parameters.append(trim(parameter))
parameter = ""
end
end
parameters.append(trim(parameter))
if len(parameters) > 1
values.add(upper(tokens[0]), parameters)
else
values.add(upper(tokens[0]), parameters[0])
end
end
end
end
end
 
return values
end
 
println get_config(args[2])</syntaxhighlight>
{{out}}
<pre>[[FULLNAME : Foo Barber], [FAVOURITEFRUIT : banana], [NEEDSPEELING : true], [OTHERFAMILY : [Rhu Barber, Harry Barber]]]</pre>
 
=={{header|Nim}}==
<syntaxhighlight lang="nim">import re, strformat, strutils, tables
 
var configs: OrderedTable[string, seq[string]]
var parsed: seq[string]
 
for line in "demo.config".lines():
let line = line.strip()
if line != "" and not line.startswith(re"#|;"):
parsed = line.split(re"\s*=\s*|\s+", 1)
configs[parsed[0].toLower()] = if len(parsed) > 1: parsed[1].split(re"\s*,\s*") else: @[]
 
for key in ["fullname", "favouritefruit", "needspeeling", "seedsremoved", "otherfamily"]:
if not configs.hasKey(key):
echo(&"{key} = false")
else:
case len(configs[key])
of 0:
echo(&"{key} = true")
of 1:
echo(&"{key} = {configs[key][0]}")
else:
for i, v in configs[key].pairs():
echo(&"{key}({i+1}) = {v}")</syntaxhighlight>
{{out}}
<pre>
fullname = Foo Barber
favouritefruit = banana
needspeeling = true
seedsremoved = false
otherfamily(1) = Rhu Barber
otherfamily(2) = Harry Barber
</pre>
 
=={{header|OCaml}}==
 
Using the library [http://homepage.mac.com/letaris/ ocaml-inifiles]:
 
<langsyntaxhighlight lang="ocaml">#use "topfind"
#require "inifiles"
open Inifiles
Line 1,095 ⟶ 3,607:
print_endline "other family:";
List.iter (Printf.printf "- %s\n") v;
;;</langsyntaxhighlight>
 
The file "conf.ini":
Line 1,128 ⟶ 3,640:
</pre>
 
{{out}}
Output:
 
<pre>$ ocaml conf.ml
Full name: Foo Barber
Line 1,140 ⟶ 3,651:
</pre>
 
=={{header|Pascal (Free Pascal)ooRexx}}==
Here's another way of doing this, which stores the values in a Rexx stem (array), and stores each value of a multivalued variable as a separate item:
<syntaxhighlight lang="oorexx">
#!/usr/bin/rexx
/*.----------------------------------------------------------------------.*/
/*|readconfig: Read keyword value pairs from a configuration file into |*/
/*| Rexx variables. |*/
/*| |*/
/*|Usage: |*/
/*| .-~/rosetta.conf-. |*/
/*|>>-readconfig-+----------------+------------------------------------><|*/
/*| |-configfilename-| |*/
/*| |-- -? ----------| |*/
/*| |-- -h ----------| |*/
/*| '- --help -------' |*/
/*| |*/
/*|where |*/
/*| configfilename |*/
/*| is the name of the configuration file to be processed. if not|*/
/*| specified, ~/rosetta.conf is used. |*/
/*| |*/
/*|All values retrieved from the configuration file are stored in |*/
/*|compound variables with the stem config. Variables with multiple |*/
/*|values have a numeric index appended, and the highest index number |*/
/*|is stored in the variable with index 0; e.g. if CONFIG.OTHERFAMILY.1 |*/
/*|and CONFIG.OTHERFAMILY.2 have values assigned, CONFIG.OTHERFAMILY.0 = |*/
/*|2. |*/
/*|-?, -h or --help all cause this documentation to be displayed. |*/
/*| |*/
/*|This program was tested using Open Object Rexx 4.1.1. It should work |*/
/*|with most other dialects as well. |*/
/*'----------------------------------------------------------------------'*/
call usage arg(1)
trace normal
signal on any name error
 
/* Prepare for processing the configuration file. */
keywords = 'FULLNAME FAVOURITEFRUIT NEEDSPEELING SEEDSREMOVED OTHERFAMILY'
 
/* Set default values for configuration variables here */
config_single?. = 1
config. = ''
config.NEEDSPEELING = 0
config.SEEDSREMOVED = 1
 
/* Validate command line inputs. */
parse arg configfilename
 
if length(configfilename) = 0 then
configfilename = '~/rosetta.conf'
 
configfile = stream(configfilename,'COMMAND','QUERY EXISTS')
 
if length(configfile) = 0 then
do
say configfilename 'was not found.'
exit 28
end
 
signal on notready /* if an I/O error occurs. */
 
/* Open the configuration file. */
response = stream(configfile,'COMMAND','OPEN READ SHARED')
 
/* Parse the contents of the configuration file into variables. */
do while lines(configfile) > 0
statement = linein(configfile)
 
select
when left(statement,1) = '#',
| left(statement,1) = ';',
| length(strip(statement)) = 0,
then /* a comment or a blank line. */
nop /* skip it. */
 
otherwise
do
if pos('=',word(statement,1)) > 0,
| left(word(statement,2),1) = '=',
then /* a keyword/value pair with = between. */
parse var statement keyword '=' value
 
else /* a keyword/value pair with no =. */
parse var statement keyword value
 
keyword = translate(strip(keyword)) /* make it uppercase. */
single? = pos(',',value) = 0 /* a single value, or multiple values? */
call value 'CONFIG_single?.'keyword,single? /* remember. */
 
if single? then
do
if length(value) > 0 then
call value 'CONFIG.'keyword,strip(value)
end /* strip keeps internal whitespace only. */
 
else /* store each value with its index. */
do v = 1 by 1 while length(value) > 0
parse var value value1 ',' value
 
if length(value1) > 0 then
do
call value 'CONFIG.'keyword'.'v,strip(value1)
call value 'CONFIG.'keyword'.0',v /* remember this. */
end
end
end
end
end
 
/* Display the values of the configuration variables. */
say 'Values associated with configuration file' configfilename':'
say
 
do while words(keywords) > 0
parse var keywords keyword keywords
 
if value('CONFIG_single?.'keyword) then
say right(keyword,20) '= "'value('CONFIG.'keyword)'"'
 
else
do
lastv = value('CONFIG.'keyword'.0')
 
do v = 1 to lastv
say right(keyword,20-(length(v)+2))'['v'] = "'value('CONFIG.'keyword'.'v)'"'
end
end
end
 
say
 
notready: /* I/O errors come here. */
filestatus = stream(configfile,'STATE')
 
if filestatus \= 'READY' then
say 'An I/O error occurred; the file status is' filestatus'.'
 
response = stream(configfile,'COMMAND','CLOSE')
 
error:
/*? = sysdumpvariables() */ /* see everything Rexx used. */
exit
 
usage: procedure
trace normal
 
if arg(1) = '-h',
| arg(1) = '-?',
| arg(1) = '--help'
then
do
line = '/*|'
say
 
do l = 3 by 1 while line~left(3) = '/*|'
line = sourceline(l)
parse var line . '/*|' text '|*/' .
say text
end
 
say
exit 0
end
return
</syntaxhighlight>
{{out}}
<pre>
$ readconfig.rex
Values associated with configuration file ~/rosetta.conf:
 
FULLNAME = "Foo Barber"
FAVOURITEFRUIT = "banana"
NEEDSPEELING = "0"
SEEDSREMOVED = "1"
OTHERFAMILY[1] = "Rhu Barber"
OTHERFAMILY[2] = "Harry Barber"
 
</pre>
<br>Leslie
 
=={{header|Pascal}}==
{{works with|Free_Pascal}}
{{libheader|SysUtils}}
 
This solution makes use of FCL-STL package shipped with FPC >= 2.6.0, moreover it can be run directly like a script (just chmod +x) using the new instantfpc feature.
 
<syntaxhighlight lang="pascal">#!/usr/bin/instantfpc
<lang Pascal>
#!/usr/bin/instantfpc
 
{$if not defined(fpc) or (fpc_fullversion < 20600)}
Line 1,244 ⟶ 3,936:
ConfigValues.Free;
ConfigStrings.Free;
end.</syntaxhighlight>
end.
</lang>
 
{{out}}
Output:
<pre>FULLNAME = Foo Barber
<pre>
FULLNAME = Foo Barber
FAVOURITEFRUIT = banana
NEEDSPEELING = True
SEEDSREMOVED = False
OTHERFAMILY(1) = Rhu Barber
OTHERFAMILY(2) = Harry Barber</pre>
 
</pre>
=={{header|Peloton}}==
Despite the discussion, this task is still a bit ambiguous. I've taken it that
* blank lines and lines beginning with # and ; should be ignored.
* an all uppercase word defines a configuration symbol
* no tail means a boolean true
* a tail including a comma means a list
* any other kind of tail is a string
 
What we end up with after processing rosetta.config are three VARs and a LST, named FAVOURITEFRUIT, FULLNAME, NEEDSPEELING and OTHERFAMILY respectively.
 
<syntaxhighlight lang="sgml"><@ DEFUDRLIT>__ReadConfigurationFile|
<@ LETSCPPNTPARSRC>Data|1</@><@ OMT> read file into locally scope variable</@>
<@ LETCGDLSTLLOSCP>List|Data</@><@ OMT> split Data into a list of lines </@>
<@ OMT> Remove comment lines, and blank lines </@>
<@ ACTOVRBEFLSTLIT>List|;</@>
<@ ACTOVRBEFLSTLIT>List|#</@>
<@ ACTRMELST>List</@>
<@ OMT> Iterate over the lines of the list </@>
<@ ITEENULSTLit>List|
<@ LETVARUPTVALLSTLIT>key|...| </@>
<@ LETVARAFTVALLSTLIT>val|...| </@>
<@ OMT> test for an empty key (in the case of a boolean) </@>
<@ TSTVARLIT>key|</@>
<@ IFE><@ LETPNTVARVARLIT>val|__True</@></@>
<@ ELS>
<@ TSTGT0ATBVARLIT>val|,</@>
<@ IFE><@ ACTEXEEMMCAP><&prot; LETCNDLSTLITLIT>&key;&pipe;&val;&pipe;, </&prot;></@></@>
<@ ELS><@ LETPNTVARVARVAR>key|val</@></@>
</@>
</@>
</@>
 
<@ ACTUDRLIT>__ReadConfigurationFile|c:\rosetta.config</@>
<@ SAYVAR>FAVOURITEFRUIT</@>
<@ SAYVAR>FULLNAME</@>
<@ SAYVAR>NEEDSPEELING</@>
<@ SAYDMPLST>OTHERFAMILY</@>
</syntaxhighlight>
 
=={{header|Perl}}==
Line 1,261 ⟶ 3,989:
This is an all-singing, all-dancing version that checks the configuration file syntax and contents and raises exceptions if it fails. (It is intentionally over-commented for pedagogical purposes.)
<langsyntaxhighlight lang="perl">my $fullname;
my $favouritefruit;
my $needspeeling;
Line 1,338 ⟶ 4,066:
}
}
</syntaxhighlight>
</lang>
 
=={{header|Perl 6Phix}}==
Normally I would recommend IupConfig, but the "standard" file format in the task description isn't even close (no [Section] headers, no '=').
This demonstrates several interesting features of Perl 6, including full grammar support, derived grammars, alternation split across derivations, and longest-token matching that works across derivations. It also shows off Perl 6's greatly cleaned up regex syntax.
<lang perl6>my $fullname;
my $favouritefruit;
my $needspeeling = False;
my $seedsremoved = False;
my @otherfamily;
 
<!--<syntaxhighlight lang="phix">-->
grammar ConfFile {
<span style="color: #004080;">integer</span> <span style="color: #000000;">fn</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">open</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"RCTEST.INI"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"r"</span><span style="color: #0000FF;">)</span>
token TOP {
<span style="color: #004080;">sequence</span> <span style="color: #000000;">lines</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">get_text</span><span style="color: #0000FF;">(</span><span style="color: #000000;">fn</span><span style="color: #0000FF;">,</span><span style="color: #004600;">GT_LF_STRIPPED</span><span style="color: #0000FF;">)</span>
:my $*linenum = 0;
<span style="color: #7060A8;">close</span><span style="color: #0000FF;">(</span><span style="color: #000000;">fn</span><span style="color: #0000FF;">)</span>
^ <fullline>* [$ || (\N*) { die "Parse failed at $0" } ]
<span style="color: #008080;">constant</span> <span style="color: #000000;">dini</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">new_dict</span><span style="color: #0000FF;">()</span>
}
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">li</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">trim</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">li</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">and</span> <span style="color: #008080;">not</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">li</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">],</span><span style="color: #008000;">"#;"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">li</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">k</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">rest</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">li</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..$]</span>
<span style="color: #000000;">li</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">li</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">k</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #000080;font-style:italic;">-- (may want upper())</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #008000;">','</span><span style="color: #0000FF;">,</span><span style="color: #000000;">rest</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">a</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #000000;">rest</span><span style="color: #0000FF;">,</span><span style="color: #008000;">','</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">j</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">a</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> <span style="color: #000000;">a</span><span style="color: #0000FF;">[</span><span style="color: #000000;">j</span><span style="color: #0000FF;">]=</span><span style="color: #7060A8;">trim</span><span style="color: #0000FF;">(</span><span style="color: #000000;">a</span><span style="color: #0000FF;">[</span><span style="color: #000000;">j</span><span style="color: #0000FF;">])</span> <span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #7060A8;">putd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">li</span><span style="color: #0000FF;">,</span><span style="color: #000000;">a</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dini</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">else</span>
<span style="color: #7060A8;">putd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">li</span><span style="color: #0000FF;">,</span><span style="color: #000000;">rest</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dini</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">else</span>
<span style="color: #7060A8;">putd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">li</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dini</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- ""</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">visitor</span><span style="color: #0000FF;">(</span><span style="color: #004080;">object</span> <span style="color: #000000;">key</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">object</span> <span style="color: #000000;">data</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">object</span> <span style="color: #000080;font-style:italic;">/*user_data*/</span><span style="color: #0000FF;">)</span>
<span style="color: #0000FF;">?{</span><span style="color: #000000;">key</span><span style="color: #0000FF;">,</span><span style="color: #000000;">data</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #7060A8;">traverse_dict</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">routine_id</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"visitor"</span><span style="color: #0000FF;">),</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dini</span><span style="color: #0000FF;">)</span>
<span style="color: #0000FF;">?</span><span style="color: #7060A8;">getd</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"FAVOURITEFRUIT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dini</span><span style="color: #0000FF;">)</span>
<!--</syntaxhighlight>-->
 
{{out}}
token fullline {
<pre>
<?before .>
{"FAVOURITEFRUIT","banana"}
{ ++$*linenum }
{"FULLNAME","Foo Barber"}
<line>
{"NEEDSPEELING",1}
[ \n || { die "Parse failed at line $*linenum" } ]
{"OTHERFAMILY",{"Rhu Barber","Harry Barber"}}
}
"banana"
</pre>
 
=={{header|Phixmonti}}==
proto token line() {{*}}
<syntaxhighlight lang="phixmonti">def optionValue
2 get "," find
if
" " "-" subst "," " " subst split
len for
var i
i get "-" " " subst
rot 1 get "(" chain print i print ") = " print swap trim print nl swap
endfor
drop drop
else
swap 1 get print " = " print swap print nl
endif
enddef
 
0 tolist
token line:misc { {} (\S+) { die "Unrecognized word: $0" } }
 
"rosetta_read.cfg" "r" fopen var file
token line:sym<comment> { ^^ [ ';' | '#' ] \N* }
token line:sym<blank> { ^^ \h* $$ }
 
file 0 > if
token line:sym<fullname> {:i fullname» <rest> { $fullname = $<rest>[0].trim } }
true
token line:sym<favouritefruit> {:i favouritefruit» <rest> { $favouritefruit = $<rest>[0].trim } }
while
token line:sym<needspeeling> {:i needspeeling» <yes> { $needspeeling = defined $<yes>[0] } }
token rest { \h* '='?file (\N*) }fgets
token yes { :i \h*dup '-1 !='? \h*if
[ trim
len 0 > if
|| ([yes|true|1])
1 get '#' != if
|| [no|false|0]
" " find var pos
|| (<?>)
pos if
] \h*
1 pos 1 - slice
}
swap len pos - pos 1 + swap slice
}
nip 2 tolist
else
"" 2 tolist
endif
0 put
else
drop
endif
else
drop
endif
true
else
drop
file fclose
false
endif
endwhile
 
len for
grammar MyConfFile is ConfFile {
get
token line:sym<otherfamily> {:i otherfamily» <many> { @otherfamily = $<many>[0]».trim} }
token many { \h*' 1 get ";" =='? ([if <![,]>2 \Nget ]*)print " = **false" ','print }nl
else 2 get "" == if 1 get print " = true" print nl
}
else optionValue
endif
endif
drop
endfor
endif</syntaxhighlight>
{{out}}
<pre>FULLNAME = Foo Barber
FAVOURITEFRUIT = banana
NEEDSPEELING = true
SEEDSREMOVED = false
OTHERFAMILY(1) = Rhu Barber
OTHERFAMILY(2) = Harry Barber</pre>
 
=={{header|PHP}}==
MyConfFile.parsefile('file.cfg');
 
Slightly modify the format of the configuration file before passing it to the internal function parse_ini_string()
 
<syntaxhighlight lang="php"><?php
 
$conf = file_get_contents('parse-conf-file.txt');
 
// Add an "=" after entry name
$conf = preg_replace('/^([a-z]+)/mi', '$1 =', $conf);
 
// Replace multiple parameters separated by commas :
// name = value1, value2
// by multiple lines :
// name[] = value1
// name[] = value2
$conf = preg_replace_callback(
'/^([a-z]+)\s*=((?=.*\,.*).*)$/mi',
function ($matches) {
$r = '';
foreach (explode(',', $matches[2]) AS $val) {
$r .= $matches[1] . '[] = ' . trim($val) . PHP_EOL;
}
return $r;
},
$conf
);
 
// Replace empty values by "true"
$conf = preg_replace('/^([a-z]+)\s*=$/mi', '$1 = true', $conf);
 
// Parse configuration file
$ini = parse_ini_string($conf);
 
echo 'Full name = ', $ini['FULLNAME'], PHP_EOL;
echo 'Favourite fruit = ', $ini['FAVOURITEFRUIT'], PHP_EOL;
echo 'Need spelling = ', (empty($ini['NEEDSPEELING']) ? 'false' : 'true'), PHP_EOL;
echo 'Seeds removed = ', (empty($ini['SEEDSREMOVED']) ? 'false' : 'true'), PHP_EOL;
echo 'Other family = ', print_r($ini['OTHERFAMILY'], true), PHP_EOL;</syntaxhighlight>
 
{{out}}
<pre>Full name = Foo Barber
Favourite fruit = banana
Need spelling = true
Seeds removed = false
Other family = Array
(
[0] => Rhu Barber
[1] => Harry Barber
)</pre>
 
=={{header|Picat}}==
<syntaxhighlight lang="picat">go =>
Vars = ["fullname","favouritefruit","needspeeling","seedsremoved","otherfamily"],
Config = read_config("read_a_configuration_file_config.cfg"),
foreach(Key in Vars)
printf("%w = %w\n", Key, Config.get(Key,false))
end,
nl.
 
% Read configuration file
read_config(File) = Config =>
Config = new_map(),
Lines = [Line : Line in read_file_lines(File), Line != [], not membchk(Line[1],"#;")],
foreach(Line in Lines)
Line := strip(Line),
once( append(Key,[' '|Value],Line) ; Key = Line, Value = true),
if find(Value,",",_,_) then
Value := [strip(Val) : Val in split(Value,",")]
end,
Key := strip(to_lowercase(Key)),
Config.put(Key,Value)
end.</syntaxhighlight>
 
{{out}}
<pre>fullname = Foo Barber
favouritefruit = banana
needspeeling = true
seedsremoved = false
otherfamily = [Rhu Barber,Harry Barber]</pre>
 
.perl.say for
:$fullname,
:$favouritefruit,
:$needspeeling,
:$seedsremoved,
:@otherfamily;</lang>
Output:
<pre>"fullname" => "Foo Barber"
"favouritefruit" => "banana"
"needspeeling" => Bool::True
"seedsremoved" => Bool::False
"otherfamily" => ["Rhu Barber", "Harry Barber"]</pre>
 
=={{header|PicoLisp}}==
<syntaxhighlight lang="picolisp">(de rdConf (File)
'read' supports only a single comment character. Therefore, we use a pipe to filter the comments.
<lang PicoLisp>(de rdConf (in File)
(pipe (in File (while (echo "#" ";") (till "^J")))
(while (read)
(set @ (or (linepack T(clip (line))) T)) ) ) )</lang>
 
Test:
(rdConf "conf.txt")
<lang PicoLisp>(off FULLNAME FAVOURITEFRUIT NEEDSPEELING SEEDSREMOVED OTHERFAMILY)
(println FULLNAME FAVOURITEFRUIT NEEDSPEELING SEEDSREMOVED OTHERFAMILY)
(rdConf "conf.txt")</lang>
(bye)</syntaxhighlight>
Output:
{{out}}
<pre>: (list FULLNAME FAVOURITEFRUIT NEEDSPEELING SEEDSREMOVED OTHERFAMILY)
-<pre> ("Foo Barber" "banana" T NIL "Rhu Barber, Harry Barber")</pre>
 
=={{header|PL/I}}==
<syntaxhighlight lang="pl/i">
<lang PL/I>
set: procedure options (main);
declare text character (100) varying;
Line 1,473 ⟶ 4,337:
 
end set;
</syntaxhighlight>
</lang>
Output,{{out}} using the given text file as input:-
<pre>
FULLNAME='Foo Barber';
Line 1,482 ⟶ 4,346:
OTHERFAMILY(1)='Rhu Barber';
OTHERFAMILY(2)='Harry Barber';
</pre>
 
=={{header|PowerShell}}==
<syntaxhighlight lang="powershell">
function Read-ConfigurationFile
{
[CmdletBinding()]
Param
(
# Path to the configuration file. Default is "C:\ConfigurationFile.cfg"
[Parameter(Mandatory=$false, Position=0)]
[string]
$Path = "C:\ConfigurationFile.cfg"
)
 
[string]$script:fullName = ""
[string]$script:favouriteFruit = ""
[bool]$script:needsPeeling = $false
[bool]$script:seedsRemoved = $false
[string[]]$script:otherFamily = @()
 
function Get-Value ([string]$Line)
{
if ($Line -match "=")
{
[string]$value = $Line.Split("=",2).Trim()[1]
}
elseif ($Line -match " ")
{
[string]$value = $Line.Split(" ",2).Trim()[1]
}
 
$value
}
 
# Process each line in file that is not a comment.
Get-Content $Path | Select-String -Pattern "^[^#;]" | ForEach-Object {
 
[string]$line = $_.Line.Trim()
 
if ($line -eq [String]::Empty)
{
# do nothing for empty lines
}
elseif ($line.ToUpper().StartsWith("FULLNAME"))
{
$script:fullName = Get-Value $line
}
elseif ($line.ToUpper().StartsWith("FAVOURITEFRUIT"))
{
$script:favouriteFruit = Get-Value $line
}
elseif ($line.ToUpper().StartsWith("NEEDSPEELING"))
{
$script:needsPeeling = $true
}
elseif ($line.ToUpper().StartsWith("SEEDSREMOVED"))
{
$script:seedsRemoved = $true
}
elseif ($line.ToUpper().StartsWith("OTHERFAMILY"))
{
$script:otherFamily = (Get-Value $line).Split(',').Trim()
}
}
 
Write-Verbose -Message ("{0,-15}= {1}" -f "FULLNAME", $script:fullName)
Write-Verbose -Message ("{0,-15}= {1}" -f "FAVOURITEFRUIT", $script:favouriteFruit)
Write-Verbose -Message ("{0,-15}= {1}" -f "NEEDSPEELING", $script:needsPeeling)
Write-Verbose -Message ("{0,-15}= {1}" -f "SEEDSREMOVED", $script:seedsRemoved)
Write-Verbose -Message ("{0,-15}= {1}" -f "OTHERFAMILY", ($script:otherFamily -join ", "))
}
</syntaxhighlight>
I stored the file in ".\temp.txt" and there is no output unless the -Verbose switch is used:
<syntaxhighlight lang="powershell">
Read-ConfigurationFile -Path .\temp.txt -Verbose
</syntaxhighlight>
{{Out}}
<pre>
VERBOSE: FULLNAME = Foo Barber
VERBOSE: FAVOURITEFRUIT = banana
VERBOSE: NEEDSPEELING = True
VERBOSE: SEEDSREMOVED = False
VERBOSE: OTHERFAMILY = Rhu Barber, Harry Barber
</pre>
Test if the variables are set:
<syntaxhighlight lang="powershell">
Get-Variable -Name fullName, favouriteFruit, needsPeeling, seedsRemoved, otherFamily
</syntaxhighlight>
{{Out}}
<pre>
Name Value
---- -----
fullName Foo Barber
favouriteFruit banana
needsPeeling True
seedsRemoved False
otherFamily {Rhu Barber, Harry Barber}
</pre>
 
=== Using Switch -Regex ===
<syntaxhighlight lang="powershell">
Function Read-ConfigurationFile {
[CmdletBinding()]
[OutputType([Collections.Specialized.OrderedDictionary])]
Param (
[Parameter(
Mandatory=$true,
Position=0
)
]
[Alias('LiteralPath')]
[ValidateScript({
Test-Path -LiteralPath $PSItem -PathType 'Leaf'
})
]
[String]
$_LiteralPath
)
 
Begin {
Function Houdini-Value ([String]$_Text) {
$__Aux = $_Text.Trim()
If ($__Aux.Length -eq 0) {
$__Aux = $true
} ElseIf ($__Aux.Contains(',')) {
$__Aux = $__Aux.Split(',') |
ForEach-Object {
If ($PSItem.Trim().Length -ne 0) {
$PSItem.Trim()
}
}
}
Return $__Aux
}
}
Process {
$__Configuration = [Ordered]@{}
# Config equivalent pattern
# Select-String -Pattern '^\s*[^\s;#=]+.*\s*$' -LiteralPath '.\filename.cfg'
Switch -Regex -File $_LiteralPath {
 
'^\s*[;#=]|^\s*$' {
Write-Verbose -Message "v$(' '*20)ignored"
Write-Verbose -Message $Matches[0]
Continue
}
 
'^([^=]+)=(.*)$' {
Write-Verbose -Message '↓← ← ← ← ← ← ← ← ← ← equal pattern'
Write-Verbose -Message $Matches[0]
$__Name,$__Value = $Matches[1..2]
$__Configuration[$__Name.Trim()] = Houdini-Value($__Value)
Continue
}
 
'^\s*([^\s;#=]+)(.*)(\s*)$' {
Write-Verbose -Message '↓← ← ← ← ← ← ← ← ← ← space or tab pattern or only name'
Write-Verbose -Message $Matches[0]
$__Name,$__Value = $Matches[1..2]
$__Configuration[$__Name.Trim()] = Houdini-Value($__Value)
Continue
}
 
}
Return $__Configuration
}
}
 
Function Show-Value ([Collections.Specialized.OrderedDictionary]$_Dictionary, $_Index, $_SubIndex) {
$__Aux = $_Index + ' = '
If ($_Dictionary[$_Index] -eq $null) {
$__Aux += $false
} ElseIf ($_Dictionary[$_Index].Count -gt 1) {
If ($_SubIndex -eq $null) {
$__Aux += $_Dictionary[$_Index] -join ','
} Else {
$__Aux = $_Index + '(' + $_SubIndex + ') = '
If ($_Dictionary[$_Index][$_SubIndex] -eq $null) {
$__Aux += $false
} Else {
$__Aux += $_Dictionary[$_Index][$_SubIndex]
}
}
} Else {
$__Aux += $_Dictionary[$_Index]
}
Return $__Aux
}
</syntaxhighlight>
Setting variable
<syntaxhighlight lang="powershell">
$Configuration = Read-ConfigurationFile -LiteralPath '.\config.cfg'
</syntaxhighlight>
 
Show variable
<syntaxhighlight lang="powershell">
$Configuration
</syntaxhighlight>
{{Out}}
<pre>
Name Value
---- -----
FULLNAME Foo Barber
FAVOURITEFRUIT banana
NEEDSPEELING True
OTHERFAMILY {Rhu Barber, Harry Barber}
</pre>
 
Using customize function
<syntaxhighlight lang="powershell">
Show-Value $Configuration 'fullname'
Show-Value $Configuration 'favouritefruit'
Show-Value $Configuration 'needspeeling'
Show-Value $Configuration 'seedsremoved'
Show-Value $Configuration 'otherfamily'
Show-Value $Configuration 'otherfamily' 0
Show-Value $Configuration 'otherfamily' 1
Show-Value $Configuration 'otherfamily' 2
</syntaxhighlight>
{{Out}}
<pre>
fullname = Foo Barber
favouritefruit = banana
needspeeling = True
seedsremoved = False
otherfamily = Rhu Barber,Harry Barber
otherfamily(0) = Rhu Barber
otherfamily(1) = Harry Barber
otherfamily(2) = False
</pre>
 
Using index variable
<syntaxhighlight lang="powershell">
'$Configuration[''fullname'']'
$Configuration['fullname']
'$Configuration.''fullname'''
$Configuration.'fullname'
'$Configuration.Item(''fullname'')'
$Configuration.Item('fullname')
'$Configuration[0]'
$Configuration[0]
'$Configuration.Item(0)'
$Configuration.Item(0)
' '
'=== $Configuration[''otherfamily''] ==='
$Configuration['otherfamily']
'=== $Configuration[''otherfamily''][0] ==='
$Configuration['otherfamily'][0]
'=== $Configuration[''otherfamily''][1] ==='
$Configuration['otherfamily'][1]
' '
'=== $Configuration.''otherfamily'' ==='
$Configuration.'otherfamily'
'=== $Configuration.''otherfamily''[0] ==='
$Configuration.'otherfamily'[0]
'=== $Configuration.''otherfamily''[1] ==='
$Configuration.'otherfamily'[1]
' '
'=== $Configuration.Item(''otherfamily'') ==='
$Configuration.Item('otherfamily')
'=== $Configuration.Item(''otherfamily'')[0] ==='
$Configuration.Item('otherfamily')[0]
'=== $Configuration.Item(''otherfamily'')[1] ==='
$Configuration.Item('otherfamily')[1]
' '
'=== $Configuration[3] ==='
$Configuration[3]
'=== $Configuration[3][0] ==='
$Configuration[3][0]
'=== $Configuration[3][1] ==='
$Configuration[3][1]
' '
'=== $Configuration.Item(3) ==='
$Configuration.Item(3)
'=== $Configuration.Item(3).Item(0) ==='
$Configuration.Item(3).Item(0)
'=== $Configuration.Item(3).Item(1) ==='
$Configuration.Item(3).Item(1)
</syntaxhighlight>
{{Out}}
<pre>
$Configuration['fullname']
Foo Barber
$Configuration.'fullname'
Foo Barber
$Configuration.Item('fullname')
Foo Barber
$Configuration[0]
Foo Barber
$Configuration.Item(0)
Foo Barber
 
=== $Configuration['otherfamily'] ===
Rhu Barber
Harry Barber
=== $Configuration['otherfamily'][0] ===
Rhu Barber
=== $Configuration['otherfamily'][1] ===
Harry Barber
 
=== $Configuration.'otherfamily' ===
Rhu Barber
Harry Barber
=== $Configuration.'otherfamily'[0] ===
Rhu Barber
=== $Configuration.'otherfamily'[1] ===
Harry Barber
 
=== $Configuration.Item('otherfamily') ===
Rhu Barber
Harry Barber
=== $Configuration.Item('otherfamily')[0] ===
Rhu Barber
=== $Configuration.Item('otherfamily')[1] ===
Harry Barber
 
=== $Configuration[3] ===
Rhu Barber
Harry Barber
=== $Configuration[3][0] ===
Rhu Barber
=== $Configuration[3][1] ===
Harry Barber
 
=== $Configuration.Item(3) ===
Rhu Barber
Harry Barber
=== $Configuration.Item(3).Item(0) ===
Rhu Barber
=== $Configuration.Item(3).Item(1) ===
Harry Barber
</pre>
 
=={{header|Python}}==
This task is not well-defined, so we have to make some assumptions (see comments in code).
<langsyntaxhighlight lang="python">def readconf(fn):
ret = {}
with file(fn) as fp:
Line 1,519 ⟶ 4,716:
conf = readconf(sys.argv[1])
for k, v in sorted(conf.items()):
print k, '=', v</langsyntaxhighlight>
 
=={{header|Racket}}==
 
Use the shared [[Racket/Options]] code.
 
<syntaxhighlight lang="racket">
=={{header|REXX}}==
#lang racket
===version 1===
No assumptions where made about what variables are (or aren't) in the configuration file.
<br>Code was written to make the postmortem report as readable as possible.
<lang rexx>/*REXX program to read a config file and assign VARs as found within. */
signal on syntax; signal on novalue /*handle REXX program errors. */
parse arg cFID _ . /*cFID = config file to be read. */
if cFID=='' then cFID='CONFIG.DAT' /*Not specified? Use the default*/
bad= /*this will contain all bad VARs.*/
varList= /*this will contain all the VARs.*/
maxLenV=0
blanks=0
hashes=0
semics=0
badVar=0
 
(require "options.rkt")
do j=0 while lines(cFID)\==0 /*J count's the file's lines. */
txt=strip(linein(cFID)) /*read a line (record) from file,*/
/*& strip leading/trailing blanks*/
if txt ='' then do; blanks=blanks+1; iterate; end
if left(txt,1)=='#' then do; hashes=hashes+1; iterate; end
if left(txt,1)==';' then do; semics=semics+1; iterate; end
eqS=pos('=',txt) /*can't use the TRANSLATE bif. */
if eqS\==0 then txt=overlay(' ',txt,eqS) /*replace 1st '=' with blank*/
parse var txt xxx value /*get the variableName and value.*/
value=strip(value) /*strip leading & trailing blanks*/
upper xxx /*uppercase the variable name. */
varList=varList xxx /*add it to the list of vARiables*/
if value='' then value='true' /*if no value, then use "true". */
 
(read-options "options-file")
if symbol(xxx)=='BAD' then do /*can REXX use the variable name?*/
(define-options fullname favouritefruit needspeeling seedsremoved otherfamily)
badVar=badVar+1; bad=bad xxx
(printf "fullname = ~s\n" fullname)
iterate
(printf "favouritefruit = ~s\n" favouritefruit)
end
(printf "needspeeling = ~s\n" needspeeling)
call value xxx,value /*now, use VALUE to set the var. */
(printf "seedsremoved = ~s\n" seedsremoved)
maxLenV=max(maxLenV,length(value)) /*maxLen of varNames, pretty disp*/
(printf "otherfamily = ~s\n" otherfamily)
end /*j*/
</syntaxhighlight>
 
{{out}}
vars=words(varList)
<pre>
say #(j) 'record's(j) "were read from file: " cFID
fullname = "Foo Barber"
if blanks\==0 then say #(blanks) 'blank record's(blanks) "were read."
favouritefruit = "banana"
if hashes\==0 then say #(hashes) 'record's(hashes) "ignored that began with a # (hash)."
needspeeling = #t
if semics\==0 then say #(semics) 'record's(semics) "ignored that began with a ; (semicolon)."
seedsremoved = #f
if badVar\==0 then say #(badVar) 'bad variable name's(badVar) 'detected:' bad
otherfamily = ("Rhu Barber" "Harry Barber")
say; say 'The list of' vars "variable"s(vars) 'and' s(vars,'their',"it's") "value"s(vars) 'follows:'; say
 
do k=1 for vars
v=word(varList,k)
say right(v,maxLenV) '=' value(v)
end /*k*/
exit /*stick a fork in it, we're done.*/
 
/*───────────────────────────────error handling subroutines and others.─*/
err: say; say; say center(' error! ',max(40,linesize()%2),"*"); say
do j=1 for arg(); say arg(j); say; end; say; exit 13
 
novalue: syntax: call err 'REXX program' condition('C') "error",,
condition('D'),'REXX source statement (line' sigl"):",,
sourceline(sigl)
 
s: if arg(1)==1 then return arg(3); return word(arg(2) 's',1)
#: return right(arg(1),length(j)+11) /*right justify a number +indent*/</lang>
'''output''' when using the input (file) as specified in the task description:
<pre style="height:35ex;overflow:scroll">
28 records were read from file: CONFIG.DAT
8 blank records were read.
15 records ignored that began with a # (hash).
1 record ignored that began with a ; (semicolon).
 
The list of 4 variables and their values follows:
 
FULLNAME = Foo Barber
FAVOURITEFRUIT = banana
NEEDSPEELING = true
OTHERFAMILY = Rhu Barber, Harry Barber
</pre>
 
===version 2={{header|Raku}}==
(formerly Perl 6)
Here's another way of doing this, which stores the values in a Rexx stem (array), and stores each value of a multivalued variable as a separate item:
{{works with|Rakudo|2020.08.1}}
<lang rexx>
This demonstrates several interesting features of Raku, including full grammar support, derived grammars, alternation split across derivations, and longest-token matching that works across derivations. It also shows off Raku's greatly cleaned up regex syntax.
#!/usr/bin/rexx
<syntaxhighlight lang="raku" line>my $fullname;
/*.----------------------------------------------------------------------.*/
my $favouritefruit;
/*|readconfig: Read keyword value pairs from a configuration file into |*/
my $needspeeling = False;
/*| Rexx variables. |*/
my $seedsremoved = False;
/*| |*/
my @otherfamily;
/*|Usage: |*/
/*| .-~/rosetta.conf-. |*/
/*|>>-readconfig-+----------------+------------------------------------><|*/
/*| |-configfilename-| |*/
/*| |-- -? ----------| |*/
/*| |-- -h ----------| |*/
/*| '- --help -------' |*/
/*| |*/
/*|where |*/
/*| configfilename |*/
/*| is the name of the configuration file to be processed. if not|*/
/*| specified, ~/rosetta.conf is used. |*/
/*| |*/
/*|All values retrieved from the configuration file are stored in |*/
/*|compound variables with the stem config. Variables with multiple |*/
/*|values have a numeric index appended, and the highest index number |*/
/*|is stored in the variable with index 0; e.g. if CONFIG.OTHERFAMILY.1 |*/
/*|and CONFIG.OTHERFAMILY.2 have values assigned, CONFIG.OTHERFAMILY.0 = |*/
/*|2. |*/
/*|-?, -h or --help all cause this documentation to be displayed. |*/
/*| |*/
/*|This program was tested using Open Object Rexx 4.1.1. It should work |*/
/*|with most other dialects as well. |*/
/*'----------------------------------------------------------------------'*/
call usage arg(1)
trace normal
signal on any name error
 
grammar ConfFile {
/* Prepare for processing the configuration file. */
token TOP {
keywords = 'FULLNAME FAVOURITEFRUIT NEEDSPEELING SEEDSREMOVED OTHERFAMILY'
:my $*linenum = 0;
^ <fullline>* [$ || (\N*) { die "Parse failed at $0" } ]
}
 
token fullline {
/* Set default values for configuration variables here */
<?before .>
config_single?. = 1
{ ++$*linenum }
config. = ''
<line>
config.NEEDSPEELING = 0
[ \n || { die "Parse failed at line $*linenum" } ]
config.SEEDSREMOVED = 1
}
 
proto token line() {*}
/* Validate command line inputs. */
parse arg configfilename
 
token line:misc { {} (\S+) { die "Unrecognized word: $0" } }
if length(configfilename) = 0 then
configfilename = '~/rosetta.conf'
 
token line:sym<comment> { ^^ [ ';' | '#' ] \N* }
configfile = stream(configfilename,'COMMAND','QUERY EXISTS')
token line:sym<blank> { ^^ \h* $$ }
 
token line:sym<fullname> {:i fullname» <rest> { $fullname = $<rest>.trim } }
if length(configfile) = 0 then
token line:sym<favouritefruit> {:i favouritefruit» <rest> { $favouritefruit = $<rest>.trim } }
do
token line:sym<needspeeling> {:i needspeeling» <yes> { $needspeeling = defined $<yes> } }
say configfilename 'was not found.'
token rest { \h* '='? (\N*) }
exit 28
token yes { :i \h* '='? \h*
end
[
|| ([yes|true|1])
|| [no|false|0]
|| (<?>)
] \h*
}
}
 
grammar MyConfFile is ConfFile {
signal on notready /* if an I/O error occurs. */
token line:sym<otherfamily> {:i otherfamily» <rest> { @otherfamily = $<rest>.split(',')».trim } }
}
 
MyConfFile.parsefile('file.cfg');
/* Open the configuration file. */
response = stream(configfile,'COMMAND','OPEN READ SHARED')
 
say "fullname: $fullname";
/* Parse the contents of the configuration file into variables. */
say "favouritefruit: $favouritefruit";
do while lines(configfile) > 0
say "needspeeling: $needspeeling";
statement = linein(configfile)
say "seedsremoved: $seedsremoved";
print "otherfamily: "; say @otherfamily.raku;</syntaxhighlight>
{{out}}
<pre>fullname: Foo Barber
favouritefruit: banana
needspeeling: True
seedsremoved: False
otherfamily: ["Rhu Barber", "Harry Barber"]
</pre>
 
=={{header|RapidQ}}==
select
<syntaxhighlight lang="vb">
when left(statement,1) = '#',
type TSettings extends QObject
| left(statement,1) = ';',
FullName as string
| length(strip(statement)) = 0,
FavouriteFruit as string
then /* a comment or a blank line. */
NeedSpelling as integer
nop /* skip it. */
SeedsRemoved as integer
OtherFamily as QStringlist
Constructor
FullName = ""
FavouriteFruit = ""
NeedSpelling = 0
SeedsRemoved = 0
OtherFamily.clear
end constructor
end type
 
Dim Settings as TSettings
otherwise
dim ConfigList as QStringList
do
dim x as integer
if pos('=',word(statement,1)) > 0,
dim StrLine as string
| left(word(statement,2),1) = '=',
dim StrPara as string
then /* a keyword/value pair with = between. */
dim StrData as string
parse var statement keyword '=' value
 
function Trim$(Expr as string) as string
else /* a keyword/value pair with no =. */
Result = Rtrim$(Ltrim$(Expr))
parse var statement keyword value
end function
 
Sub ConfigOption(PData as string)
keyword = translate(strip(keyword)) /* make it uppercase. */
dim x as integer
single? = pos(',',value) = 0 /* a single value, or multiple values? */
for x = 1 to tally(PData, ",") +1
call value 'CONFIG_single?.'keyword,single? /* remember. */
Settings.OtherFamily.AddItems Trim$(field$(PData, "," ,x))
next
end sub
 
Function ConfigBoolean(PData as string) as integer
if single? then
PData = doTrim$(PData)
Result = iif(lcase$(PData)="true" or PData="1" or PData="", 1, 0)
if length(value) > 0 then
end function
call value 'CONFIG.'keyword,strip(value)
end /* strip keeps internal whitespace only. */
 
sub ReadSettings
else /* store each value with its index. */
ConfigList.LoadFromFile("Rosetta.cfg")
do v = 1 by 1 while length(value) > 0
ConfigList.text = REPLACESUBSTR$(ConfigList.text,"="," ")
parse var value value1 ',' value
 
for x = 0 to ConfigList.ItemCount -1
if length(value1) > 0 then
StrLine = doTrim$(ConfigList.item(x))
StrPara = Trim$(field$(StrLine," ",1))
call value 'CONFIG.'keyword'.'v,strip(value1)
StrData = Trim$(lTrim$(StrLine - StrPara))
call value 'CONFIG.'keyword'.0',v /* remember this. */
end
Select case endUCase$(StrPara)
case "FULLNAME" : Settings.FullName = StrData
end
case "FAVOURITEFRUIT" : Settings.FavouriteFruit = StrData
end
case "NEEDSPEELING" : Settings.NeedSpelling = ConfigBoolean(StrData)
end
case "SEEDSREMOVED" : Settings.SeedsRemoved = ConfigBoolean(StrData)
case "OTHERFAMILY" : Call ConfigOption(StrData)
end select
next
end sub
 
Call ReadSettings
/* Display the values of the configuration variables. */
</syntaxhighlight>
say 'Values associated with configuration file' configfilename':'
say
 
{{out}}
do while words(keywords) > 0
<pre>
parse var keywords keyword keywords
Settings.FullName = Foo Barber
Settings.FavouriteFruit = banana
Settings.NeedSpelling = 1
Settings.SeedsRemoved = 0
Settings.OtherFamily.Item(0) = Rhu Barber
Settings.OtherFamily.Item(1) = Harry Barber
</pre>
=={{header|Red}}==
<syntaxhighlight lang="rebol">Red ["Read a config file"]
 
remove-each l lines: read/lines %file.conf [any [empty? l #"#" = l/1]]
if value('CONFIG_single?.'keyword) then
foreach line lines [
say right(keyword,20) '= "'value('CONFIG.'keyword)'"'
foo: parse line [collect [keep to [" " | end] skip keep to end]]
either foo/1 = #";" [set to-word foo/2 false][
set to-word foo/1 any [
all [find foo/2 #"," split foo/2 ", "]
foo/2
true
]
]
]
foreach w [fullname favouritefruit needspeeling seedsremoved otherfamily][
prin [pad w 15 ": "] probe get w
]</syntaxhighlight>
 
{{out}}
else
<pre>fullname : "Foo Barber"
do
favouritefruit : "banana"
lastv = value('CONFIG.'keyword'.0')
needspeeling : true
seedsremoved : false
otherfamily : ["Rhu Barber" "Harry Barber"]
</pre>
 
=={{header|REXX}}==
do v = 1 to lastv
No assumptions were made about what variables are (or aren't) in the configuration file.
say right(keyword,20-(length(v)+2))'['v'] = "'value('CONFIG.'keyword'.'v)'"'
<br>Code was written to make the postmortem report as readable as possible.
end
<syntaxhighlight lang="rexx">/*REXX program reads a config (configuration) file and assigns VARs as found within. */
end
signal on syntax; signal on novalue /*handle REXX source program errors. */
end
parse arg cFID _ . /*cFID: is the CONFIG file to be read.*/
if cFID=='' then cFID='CONFIG.DAT' /*Not specified? Then use the default.*/
bad= /*this will contain all the bad VARs. */
varList= /* " " " " " good " */
maxLenV=0; blanks=0; hashes=0; semics=0; badVar=0 /*zero all these variables.*/
 
do j=0 while lines(cFID)\==0 /*J: it counts the lines in the file.*/
say
txt=strip(linein(cFID)) /*read a line (record) from the file, */
/* ··· & strip leading/trailing blanks*/
if txt ='' then do; blanks=blanks+1; iterate; end /*count # blank lines.*/
if left(txt,1)=='#' then do; hashes=hashes+1; iterate; end /* " " lines with #*/
if left(txt,1)==';' then do; semics=semics+1; iterate; end /* " " " " ;*/
eqS=pos('=',txt) /*we can't use the TRANSLATE BIF. */
if eqS\==0 then txt=overlay(' ',txt,eqS) /*replace the first '=' with a blank.*/
parse var txt xxx value; upper xxx /*get the variable name and it's value.*/
value=strip(value) /*strip leading and trailing blanks. */
if value='' then value='true' /*if no value, then use "true". */
if symbol(xxx)=='BAD' then do /*can REXX utilize the variable name ? */
badVar=badVar+1; bad=bad xxx; iterate /*append to list*/
end
varList=varList xxx /*add it to the list of good variables.*/
call value xxx,value /*now, use VALUE to set the variable. */
maxLenV=max(maxLenV,length(value)) /*maxLen of varNames, pretty display. */
end /*j*/
 
vars=words(varList); @ig= 'ignored that began with a'
notready: /* I/O errors come here. */
say #(j) 'record's(j) "were read from file: " cFID
filestatus = stream(configfile,'STATE')
if blanks\==0 then say #(blanks) 'blank record's(blanks) "were read."
 
if hashes\==0 then say #(hashes) 'record's(hashes) @ig "# (hash)."
if filestatus \= 'READY' then
if semics\==0 then say #(semics) 'record's(semics) @ig "; (semicolon)."
say 'An I/O error occurred; the file status is' filestatus'.'
if badVar\==0 then say #(badVar) 'bad variable name's(badVar) 'detected:' bad
 
say; say 'The list of' vars "variable"s(vars) 'and' s(vars,'their',"it's"),
response = stream(configfile,'COMMAND','CLOSE')
"value"s(vars) 'follows:'
 
say; do k=1 for vars
error:
v=word(varList,k)
/*? = sysdumpvariables() */ /* see everything Rexx used. */
say right(v,maxLenV) '=' value(v)
exit
end /*k*/
 
say; exit /*stick a fork in it, we're all done. */
usage: procedure
/*──────────────────────────────────────────────────────────────────────────────────────*/
trace normal
s: if arg(1)==1 then return arg(3); return word(arg(2) 's',1)
 
#: return right(arg(1),length(j)+11) /*right justify a number & also indent.*/
if arg(1) = '-h',
err: do j=1 for arg(); say '***error*** ' arg(j); say; end /*j*/; exit 13
| arg(1) = '-?',
novalue: syntax: call err 'REXX program' condition('C') "error",,
| arg(1) = '--help'
condition('D'),'REXX source statement (line' sigl"):",sourceline(sigl)
then
</syntaxhighlight>
do
'''output''' &nbsp; when using the input (file) as specified in the task description:
line = '/*|'
say
 
do l = 3 by 1 while line~left(3) = '/*|'
line = sourceline(l)
parse var line . '/*|' text '|*/' .
say text
end
 
say
exit 0
end
return
</lang>
<br>It produces the following output:
<br>
<pre>
28 records were read from file: CONFIG.DAT
$ readconfig.rex
8 blank records were read.
Values associated with configuration file ~/rosetta.conf:
15 records ignored that began with a # (hash).
1 record ignored that began with a ; (semicolon).
 
The list of 4 variables and their values follows:
FULLNAME = "Foo Barber"
FAVOURITEFRUIT = "banana"
NEEDSPEELING = "0"
SEEDSREMOVED = "1"
OTHERFAMILY[1] = "Rhu Barber"
OTHERFAMILY[2] = "Harry Barber"
 
FULLNAME = Foo Barber
FAVOURITEFRUIT = banana
NEEDSPEELING = true
OTHERFAMILY = Rhu Barber, Harry Barber
</pre>
<br>Leslie
 
=={{header|Ruby}}==
<syntaxhighlight lang ="ruby">fullname = favouritefruit = needspeeling = seedsremoved = false""
needspeeling = seedsremoved = false
otherfamily = []
 
IO.foreach("fruitconfig.conffile") do |line|
line.chomp!
key, value = line.split(nil, 2)
Line 1,793 ⟶ 4,984:
when "NEEDSPEELING"; needspeeling = true
when "SEEDSREMOVED"; seedsremoved = true
when "OTHERFAMILY"; otherfamily = value.split(",").map(&:strip)
when /^./; puts "#{key}: unknown key"
end
end
 
puts "fullname = #{fullname}"
puts "favouritefruit = #{favouritefruit}"
puts "needspeeling = #{needspeeling}"
puts "seedsremoved = #{seedsremoved}"</lang>
otherfamily.each_with_index do |name, i|
puts "otherfamily(#{i+1}) = #{name}"
end</syntaxhighlight>
 
{{out}}
<pre>
fullname = Foo Barber
favouritefruit = banana
needspeeling = true
seedsremoved = false
otherfamily(1) = Rhu Barber
otherfamily(2) = Harry Barber
</pre>
 
=={{header|Run BASIC}}==
<syntaxhighlight lang="runbasic">dim param$(6)
dim paramVal$(6)
param$(1) = "fullname"
param$(2) = "favouritefruit"
param$(3) = "needspeeling"
param$(4) = "seedsremoved"
param$(5) = "otherfamily"
for i = 1 to 6
paramVal$(i) = "false"
next i
 
open DefaultDir$ + "\public\a.txt" for binary as #f
while not(eof(#f))
line input #f, a$
a$ = trim$(a$)
if instr("#;",left$(a$,1)) = 0 and a$ <> "" then
thisParam$ = lower$(word$(a$,1," "))
for i = 1 to 5
if param$(i) = thisParam$ then
paramVal$(i) = "true"
aa$ = trim$(mid$(a$,len(thisParam$)+2))
if aa$ <> "" then paramVal$(i) = aa$
end if
next i
end if
wend
close #f
for i = 1 to 5
if instr(paramVal$(i),",") > 0 then
for j = 1 to 2
print param$(i);"(";j;")";chr$(9);trim$(word$(paramVal$(i),j,","))
next j
else
print param$(i);chr$(9);paramVal$(i)
end if
next i</syntaxhighlight>
{{out}}
<pre>
fullname Foo Barber
favouritefruit banana
needspeeling true
seedsremoved false
otherfamily(1) Rhu Barber
otherfamily(2) Harry Barber
</pre>
 
=={{header|Rust}}==
<syntaxhighlight lang="rust">use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::iter::FromIterator;
use std::path::Path;
 
fn main() {
let path = String::from("file.conf");
let cfg = config_from_file(path);
println!("{:?}", cfg);
}
 
fn config_from_file(path: String) -> Config {
let path = Path::new(&path);
let file = File::open(path).expect("File not found or cannot be opened");
let content = BufReader::new(&file);
let mut cfg = Config::new();
 
for line in content.lines() {
let line = line.expect("Could not read the line");
// Remove whitespaces at the beginning and end
let line = line.trim();
 
// Ignore comments and empty lines
if line.starts_with("#") || line.starts_with(";") || line.is_empty() {
continue;
}
 
// Split line into parameter name and rest tokens
let tokens = Vec::from_iter(line.split_whitespace());
let name = tokens.first().unwrap();
let tokens = tokens.get(1..).unwrap();
 
// Remove the equal signs
let tokens = tokens.iter().filter(|t| !t.starts_with("="));
// Remove comment after the parameters
let tokens = tokens.take_while(|t| !t.starts_with("#") && !t.starts_with(";"));
// Concat back the parameters into one string to split for separated parameters
let mut parameters = String::new();
tokens.for_each(|t| { parameters.push_str(t); parameters.push(' '); });
// Splits the parameters and trims
let parameters = parameters.split(',').map(|s| s.trim());
// Converts them from Vec<&str> into Vec<String>
let parameters: Vec<String> = parameters.map(|s| s.to_string()).collect();
// Setting the config parameters
match name.to_lowercase().as_str() {
"fullname" => cfg.full_name = parameters.get(0).cloned(),
"favouritefruit" => cfg.favourite_fruit = parameters.get(0).cloned(),
"needspeeling" => cfg.needs_peeling = true,
"seedsremoved" => cfg.seeds_removed = true,
"otherfamily" => cfg.other_family = Some(parameters),
_ => (),
}
}
 
cfg
}
 
#[derive(Clone, Debug)]
struct Config {
full_name: Option<String>,
favourite_fruit: Option<String>,
needs_peeling: bool,
seeds_removed: bool,
other_family: Option<Vec<String>>,
}
 
impl Config {
fn new() -> Config {
Config {
full_name: None,
favourite_fruit: None,
needs_peeling: false,
seeds_removed: false,
other_family: None,
}
}
}</syntaxhighlight>
 
{{out}}
<pre>
Config { full_name: Some("Foo Barber"), favourite_fruit: Some("banana"), needs_peeling: true, seeds_removed: false, other_family: Some(["Rhu Barber", "Harry Barber"]) }
</pre>
 
=={{header|Scala}}==
A "one liner" version which:
* Filters out empty and comment lines
* Splits field name and value(s)
* Adds "true" to value-less fields
* Converts values to (k, List(values)
* Converts the entire collection to a Map
 
<syntaxhighlight lang="scala">val conf = scala.io.Source.fromFile("config.file").
getLines.
toList.
filter(_.trim.size > 0).
filterNot("#;" contains _(0)).
map(_ split(" ", 2) toList).
map(_ :+ "true" take 2).
map {
s:List[String] => (s(0).toLowerCase, s(1).split(",").map(_.trim).toList)
}.toMap</syntaxhighlight>
 
Test code:
<syntaxhighlight lang="scala">
for ((k,v) <- conf) {
if (v.size == 1)
println("%s: %s" format (k, v(0)))
else
for (i <- 0 until v.size)
println("%s(%s): %s" format (k, i+1, v(i)))
 
}
</syntaxhighlight>
{{out}}
<pre>
fullname: Foo Barber
favouritefruit: banana
needspeeling: true
otherfamily(1): Rhu Barber
otherfamily(2): Harry Barber
</pre>
 
=={{header|Seed7}}==
Line 1,809 ⟶ 5,187:
and [http://seed7.sourceforge.net/libraries/scanfile.htm#getLine%28inout_file%29 getLine].
 
<langsyntaxhighlight lang="seed7">$ include "seed7_05.s7i";
include "scanfile.s7i";
 
Line 1,857 ⟶ 5,235:
writeln(("otherfamily[" <& index <& "]:") rpad 16 <& otherfamily[index]);
end for;
end func;</langsyntaxhighlight>
 
{{out}}
Output:
<pre>
fullname: Foo Barber
Line 1,868 ⟶ 5,246:
otherfamily[2]: Harry Barber
</pre>
 
=={{header|SenseTalk}}==
<syntaxhighlight lang="sensetalk">
// read the configuration file and get a list of just the interesting lines
set lines to each line of file "config.txt" where char 1 of each isn't in ("#", ";", "")
 
set the listFormat's quotes to quote -- be sure to quote values for evaluating
 
repeat with each configLine in lines
put word 1 of configLine into varName
insert varName into variableNames -- make a list of all config variables
put (words 2 to last of configLine) split by comma into values
put trim of each item of values into values -- trim any leading/trailing spaces
if values is empty then set values to true -- no value means boolean true
do "set" && varName && "to" && values -- assign value to variable
end repeat
 
repeat with each name in variableNames
put "Variable" && name && "is" && value(name)
end repeat
</syntaxhighlight>
Output:
<syntaxhighlight lang="sensetalk">
Variable FULLNAME is Foo Barber
Variable FAVOURITEFRUIT is banana
Variable NEEDSPEELING is True
Variable OTHERFAMILY is ("Rhu Barber","Harry Barber")
</syntaxhighlight>
 
=={{header|Sidef}}==
<syntaxhighlight lang="ruby">var fullname = (var favouritefruit = "");
var needspeeling = (var seedsremoved = false);
var otherfamily = [];
 
ARGF.each { |line|
var(key, value) = line.strip.split(/\h+/, 2)...;
 
given(key) {
when (nil) { }
when (/^([#;]|\h*$)/) { }
when ("FULLNAME") { fullname = value }
when ("FAVOURITEFRUIT") { favouritefruit = value }
when ("NEEDSPEELING") { needspeeling = true }
when ("SEEDSREMOVED") { seedsremoved = true }
when ("OTHERFAMILY") { otherfamily = value.split(',')»strip»() }
default { say "#{key}: unknown key" }
}
}
 
say "fullname = #{fullname}";
say "favouritefruit = #{favouritefruit}";
say "needspeeling = #{needspeeling}";
say "seedsremoved = #{seedsremoved}";
 
otherfamily.each_kv {|i, name|
say "otherfamily(#{i+1}) = #{name}";
}</syntaxhighlight>
{{out}}
<pre>
fullname = Foo Barber
favouritefruit = banana
needspeeling = true
seedsremoved = false
otherfamily(1) = Rhu Barber
otherfamily(2) = Harry Barber
</pre>
 
=={{header|Smalltalk}}==
{{works with|Smalltalk/X}}
This code retrieves the configuration values as a Dictionary; code to set an object's instance variables follows below:
<syntaxhighlight lang="smalltalk">dict := Dictionary new.
configFile asFilename readingLinesDo:[:line |
(line isEmpty or:[ line startsWithAnyOf:#('#' ';') ]) ifFalse:[
s := line readStream.
(s skipSeparators; atEnd) ifFalse:[
|optionName values|
optionName := s upToSeparator.
values := (s upToEnd asCollectionOfSubstringsSeparatedBy:$,)
collect:[:each | each withoutSeparators]
thenSelect:[:vals | vals notEmpty].
dict at:optionName asLowercase put:(values isEmpty
ifTrue:[true]
ifFalse:[
values size == 1
ifTrue:[values first]
ifFalse:[values]]).
]
].
]</syntaxhighlight>
gives us in dict Dictionary ('fullname'->'Foo Barber' 'needspeeling'->true 'favouritefruit'->'banana' 'otherfamily'->OrderedCollection('Rhu Barber' 'Harry Barber'))
 
assuming that the target object has setters for each option name, we could write:
<syntaxhighlight lang="smalltalk">dict keysAndValuesDo:[:eachOption :eachValue |
someObject
perform:(eachOption,':') asSymbol with:eachValue
ifNotUnderstood: [ self error: 'unknown option: ', eachOption ]
]</syntaxhighlight>
or assign variables with:
<syntaxhighlight lang="smalltalk">fullname := dict at: 'fullname' ifAbsent:false.
needspeeling := dict at: 'needspeeling' ifAbsent:false.
favouritefruit := dict at: 'favouritefruit' ifAbsent:false.
otherfamily := dict at: 'otherfamily' ifAbsent:false.
seedsremoved := dict at: 'seedsremoved' ifAbsent:false.
</syntaxhighlight>
 
=={{header|Tcl}}==
This code stores the configuration values in a global array (<code>cfg</code>) so they can't pollute the global namespace in unexpected ways.
<langsyntaxhighlight lang="tcl">proc readConfig {filename {defaults {}}} {
global cfg
# Read the file in
Line 1,916 ⟶ 5,399:
puts "Peeling? $cfg(needspeeling)"
puts "Unseeded? $cfg(seedsremoved)"
puts "Family: $cfg(otherfamily)"</langsyntaxhighlight>
 
=={{header|TXR}}==
Prove the logic by transliterating to a different syntax:
<langsyntaxhighlight lang="txr">@(collect)
@ (cases)
#@/.*/
Line 1,944 ⟶ 5,427:
@ (end)
@(end)
</syntaxhighlight>
</lang>
 
Sample run:
Line 1,953 ⟶ 5,436:
NEEDSPEELING = true;
OTHERFAMILY = { Rhu Barber, Harry Barber };
</pre>
 
=={{header|UNIX Shell}}==
No effort is made to make an exception for SEEDSREMOVED: lines beginning with a semi-colon are treated as comments.
No expectations are made about what variables will be seen in the config file.
 
{{works with|bash}}
<syntaxhighlight lang="bash">readconfig() (
# redirect stdin to read from the given filename
exec 0<"$1"
 
declare -l varname
while IFS=$' =\t' read -ra words; do
# is it a comment or blank line?
if [[ ${#words[@]} -eq 0 || ${words[0]} == ["#;"]* ]]; then
continue
fi
 
# get the variable name
varname=${words[0]}
 
# split all the other words by comma
value="${words[*]:1}"
oldIFS=$IFS IFS=,
values=( $value )
IFS=$oldIFS
 
# assign the other words to a "scalar" variable or array
case ${#values[@]} in
0) printf '%s=true\n' "$varname" ;;
1) printf '%s=%q\n' "$varname" "${values[0]}" ;;
*) n=0
for value in "${values[@]}"; do
value=${value# }
printf '%s[%d]=%q\n' "$varname" $((n++)) "${value% }"
done
;;
esac
done
)
 
# parse the config file and evaluate the output in the current shell
source <( readconfig config.file )
 
echo "fullname = $fullname"
echo "favouritefruit = $favouritefruit"
echo "needspeeling = $needspeeling"
echo "seedsremoved = $seedsremoved"
for i in "${!otherfamily[@]}"; do
echo "otherfamily[$i] = ${otherfamily[i]}"
done</syntaxhighlight>
 
{{out}}
<pre>fullname = Foo Barber
favouritefruit = banana
needspeeling = true
seedsremoved =
otherfamily[0] = Rhu Barber
otherfamily[1] = Harry Barber</pre>
 
=={{header|VBScript}}==
<syntaxhighlight lang="vb">
Set ofso = CreateObject("Scripting.FileSystemObject")
Set config = ofso.OpenTextFile(ofso.GetParentFolderName(WScript.ScriptFullName)&"\config.txt",1)
 
config_out = ""
 
Do Until config.AtEndOfStream
line = config.ReadLine
If Left(line,1) <> "#" And Len(line) <> 0 Then
config_out = config_out & parse_var(line) & vbCrLf
End If
Loop
 
WScript.Echo config_out
 
Function parse_var(s)
'boolean false
If InStr(s,";") Then
parse_var = Mid(s,InStr(1,s,";")+2,Len(s)-InStr(1,s,";")+2) & " = FALSE"
'boolean true
ElseIf UBound(Split(s," ")) = 0 Then
parse_var = s & " = TRUE"
'multiple parameters
ElseIf InStr(s,",") Then
var = Left(s,InStr(1,s," ")-1)
params = Split(Mid(s,InStr(1,s," ")+1,Len(s)-InStr(1,s," ")+1),",")
n = 1 : tmp = ""
For i = 0 To UBound(params)
parse_var = parse_var & var & "(" & n & ") = " & LTrim(params(i)) & vbCrLf
n = n + 1
Next
'single var and paramater
Else
parse_var = Left(s,InStr(1,s," ")-1) & " = " & Mid(s,InStr(1,s," ")+1,Len(s)-InStr(1,s," ")+1)
End If
End Function
 
config.Close
Set ofso = Nothing
</syntaxhighlight>
 
{{Out}}
<pre>
FULLNAME = Foo Barber
FAVOURITEFRUIT = banana
NEEDSPEELING = TRUE
SEEDSREMOVED = FALSE
OTHERFAMILY(1) = Rhu Barber
OTHERFAMILY(2) = Harry Barber
</pre>
 
=={{header|Vedit macro language}}==
 
<langsyntaxhighlight lang="vedit">#11 = 0 // needspeeling = FALSE
#12 = 0 // seedsremoved = FALSE
Reg_Empty(21) // fullname
Line 1,991 ⟶ 5,584:
Message("fullname = ") Reg_Type(21) TN
Message("favouritefruit = ") Reg_Type(22) TN
Message("otherfamily = ") Reg_Type(23) TN</langsyntaxhighlight>
 
{{out}}
Output:
<pre>
needspeeling = 1
Line 2,004 ⟶ 5,597:
=={{header|Visual Basic}}==
 
<langsyntaxhighlight lang="vb">' Configuration file parser routines.
'
' (c) Copyright 1993 - 2011 Mark Hobley
Line 2,208 ⟶ 5,801:
Public Function btrim$(arg$)
btrim$ = LTrim$(RTrim$(arg$))
End Function</langsyntaxhighlight>
 
=={{header|Wren}}==
{{trans|Kotlin}}
{{libheader|Wren-ioutil}}
Includes 'seeds removed' in the map (with a default value of false) even though it's commented out of the configuration file.
<syntaxhighlight lang="wren">import "io" for File
import "./ioutil" for FileUtil
 
class Configuration {
construct new(map) {
_fullName = map["fullName"]
_favouriteFruit = map["favouriteFruit"]
_needsPeeling = map["needsPeeling"]
_seedsRemoved = map["seedsRemoved"]
_otherFamily = map["otherFamily"]
}
 
toString {
return [
"Full name = %(_fullName)",
"Favourite fruit = %(_favouriteFruit)",
"Needs peeling = %(_needsPeeling)",
"Seeds removed = %(_seedsRemoved)",
"Other family = %(_otherFamily)"
].join("\n")
}
}
 
var commentedOut = Fn.new { |line| line.startsWith("#") || line.startsWith(";") }
 
var toMapEntry = Fn.new { |line|
var ix = line.indexOf(" ")
if (ix == -1) return MapEntry.new(line, "")
return MapEntry.new(line[0...ix], line[ix+1..-1])
}
 
var fileName = "configuration.txt"
var lines = File.read(fileName).trimEnd().split(FileUtil.lineBreak)
var mapEntries = lines.map { |line| line.trim() }.
where { |line| line != "" }.
where { |line| !commentedOut.call(line) }.
map { |line| toMapEntry.call(line) }
var configurationMap = { "needsPeeling": false, "seedsRemoved": false }
for (me in mapEntries) {
if (me.key == "FULLNAME") {
configurationMap["fullName"] = me.value
} else if (me.key == "FAVOURITEFRUIT") {
configurationMap["favouriteFruit"] = me.value
} else if (me.key == "NEEDSPEELING") {
configurationMap["needsPeeling"] = true
} else if (me.key == "OTHERFAMILY") {
configurationMap["otherFamily"] = me.value.split(" , ").map { |s| s.trim() }.toList
} else if (me.key == "SEEDSREMOVED") {
configurationMap["seedsRemoved"] = true
} else {
System.print("Encountered unexpected key %(me.key)=%(me.value)")
}
}
System.print(Configuration.new(configurationMap))</syntaxhighlight>
 
{{out}}
<pre>
Full name = Foo Barber
Favourite fruit = banana
Needs peeling = true
Seeds removed = false
Other family = [Rhu Barber, Harry Barber]
</pre>
 
=={{header|Yabasic}}==
<syntaxhighlight lang="yabasic">a = open("rosetta_read.cfg")
 
while(not eof(#a))
FLAG = true : REMARK = false
line input #a line$
line$ = trim$(line$)
ll = len(line$)
c$ = left$(line$, 1)
switch(c$)
case "": case "#": REMARK = true : break
case ";": FLAG = false : line$ = trim$(right$(line$, ll - 1)) : break
default: MULTI = instr(line$, ",")
end switch
 
if not REMARK then
GAP = instr(line$, "=") : if not GAP GAP = instr(line$, " ")
if not GAP then
print line$, " = ";
if FLAG then print "true" else print "false" end if
else
if MULTI then
count = 1 : SG = GAP
repeat
print left$(line$, GAP - 1), "(", count, ") = ", trim$(mid$(line$, SG + 1, MULTI - SG - 1))
count = count + 1
SG = MULTI + 1 : MULTI = instr(line$, ",", SG)
if not MULTI MULTI = ll + 1
until(SG > ll)
else
print left$(line$, GAP - 1), " = ", trim$(right$(line$, ll - GAP))
end if
end if
end if
wend
 
close #a</syntaxhighlight>
 
=={{header|zkl}}==
First, a simple parser that knows nothing about contents, just format.
<syntaxhighlight lang="zkl">fcn readConfigFile(config){ //--> read only dictionary
conf:=Dictionary();
foreach line in (config){
line=line.strip();
if (not line or "#"==line[0] or ";"==line[0]) continue;
line=line.replace("\t"," ");
n:=line.find(" ");
if (Void==n) conf[line.toLower()]=True; // eg NEEDSPEELING
else{
key:=line[0,n].toLower(); line=line[n,*];
n=line.find(",");
if (Void!=n) conf[key]=line.split(",").apply("strip").filter();
else conf[key]=line;
}
}
conf.makeReadOnly();
}
 
conf:=readConfigFile(File("foo.conf"));</syntaxhighlight>
Which may be good enough; query the hash table to get a set option or a default if it wasn't set:
<syntaxhighlight lang="zkl">foreach k,v in (conf){ println(k," : ",v) }
println("Value of seedsremoved = ",conf.find("seedsremoved",False));</syntaxhighlight>
{{out}}
<pre>
needspeeling : True
otherfamily : L("Rhu Barber","Harry Barber")
favouritefruit : banana
fullname : Foo Barber
Value of seedsremoved = False
</pre>
If your program actually wants to use the options as variables, the following sets variables to values in the config file, ignoring misspellings, or values you don't care about. You are not allowed to create variables "on the fly".
<syntaxhighlight lang="zkl">var needspeeling,otherfamily,favouritefruit,fullname,seedsremoved;
foreach k,v in (conf){ try{ setVar(k,v) }catch{} };
foreach k,v in (vars){ println(k," : ",v) }
println("Value of seedsremoved = ",seedsremoved);</syntaxhighlight>
{{out}}
<pre>
favouritefruit : banana
fullname : Foo Barber
needspeeling : True
otherfamily : L("Rhu Barber","Harry Barber")
seedsremoved : Void
Value of seedsremoved = Void
</pre>
 
 
Line 2,214 ⟶ 5,960:
{{omit from|Lilypond}}
{{omit from|Openscad}}
{{omit from|Z80 Assembly|Label names do not exist at runtime.}}
 
[[Category:Initialization]]
9,485

edits