Ini file library

Monkey Forums/Monkey Beginners/Ini file library

hub(Posted 2015) [#1]
Hi !
I'm searching a way to parse an short ini file. (i only want read values)
So is there already a monkey code example to do this ?
Many Thanks.


Shinkiro1(Posted 2015) [#2]
Hi, I have written a module for this.

'--------------------------------------------------------------------------
' File Format: Custom ini file
'
' [ Section ]
' value = property
' multilineValue = {--
' 0 1 2 3 4 5 6 7 8
' 9 10 11 12 13 14 15
' 16 17 18 19 20 21
' --}
'--------------------------------------------------------------------------

Strict
Import mojo.app

Class IniFile

	Method New(path:String)
		Local text:= LoadString(path)
		If text
			InitWithString(text, path)
		End
	End
	
	Method InitWithString:Void(iniString:String, name:String = "unnamed")
		Local lines:String[] = iniString.Split("~n")
		Local currentSection:IniSection
		Local multilineMode:Bool = False
		Local multilineData:String
		Local multilineProp:String
		sections = New StringMap<IniSection>
		AddSection(GLOBALS)
		currentSection = GetGlobals()
		
		For Local i:Int = 0 Until lines.Length
			Local line:String = lines[i].Trim()
			
			'comments
			If line.Length = 0 Or line.StartsWith("#")
				'Do nothing
				
			'read multiple lines
			ElseIf multilineMode
				If line = MULTILINE_END
					multilineMode = False
					SetProperty(currentSection, multilineProp, multilineData)
				Else
					multilineData += line + " "
				End
				
			'sections	
			ElseIf line.StartsWith("[")
				If line.EndsWith("]")
					Local sectionID:String = line[1..-1].Trim()
					If sections.Contains(sectionID)
						GenerateError("Section " + sectionID + " has already been declared! (on line " + (i+1) + ")", name)
					Else
						AddSection(sectionID)
						currentSection = GetSection(sectionID)
					End
				Else
					GenerateError("Sections must be closed! (on line " + (i+1) + ")", name)
				End
			
			'properties
			Else
				Local delimiterIndex:Int = line.Find(DELIMITER)
				If delimiterIndex = -1
					GenerateError("Attempting to parse line " + (i+1) + ": " + line + "~nbut could not find the delimiter " + DELIMITER, name)
				End
				Local leftSide:String = line[0..delimiterIndex].Trim()
				Local rightSide:String = line[delimiterIndex+1..].Trim()
				If rightSide = MULTILINE_START
					multilineMode = True
					multilineProp = leftSide
				Else
					SetProperty(currentSection, leftSide, rightSide)
				End
				
			End
		Next
		
		If multilineMode
			GenerateError("You forgot to close a multiline property with ~q" + MULTILINE_START + "~q", name)
		End
	End

	Method Get:String(value:String, fallback:String = "")
		Local g:= GetGlobals()
		If g
			Return g.GetString(value, fallback)
		End
		Return fallback
	End

	Method GetSection:IniSection(section:String)
		Return sections.Get(section)
	End
	
	Method GetGlobals:IniSection()
		Return GetSection(GLOBALS)
	End
	
	Method AddSection:Void(id:String)
		Local s:= New IniSection
		s.id = id
		s.data = New StringMap<String>
		sections.Add(id, s)
	End
	
	Method SetProperty:Void(section:IniSection, prop:String, value:String)
		If Not section Then Return
		If section.data.Contains(prop)
			section.data.Set(prop, value)
		Else
			section.data.Add(prop, value)
		End
	End
	
	Private
	Field sections:StringMap<IniSection> = New StringMap<IniSection>
	Const DELIMITER:String = "="
	Const GLOBALS:String = "_GLOBALS_"
	Const MULTILINE_START:String = "{--"
	Const MULTILINE_END:String = "--}"
	
	Method GenerateError:Void(message:String, name:String)
		Error("IniFile " + name + "~n" + message)
		sections = Null
	End
	
	'could be used for quote parsing
	Method ParseString:String(rawString:String)
		Local trimmed:String = rawString.Trim()
		Local findQuote:Int = trimmed.Find("~q")
		
		If findQuote = -1 Then Return trimmed
		
		If trimmed[0] = "~q"[0] And trimmed[trimmed.Length-1] = "~q"[0]
			Return trimmed[1..trimmed.Length-1]
		End
		
		Return ""	
	End
	
End


Class IniSection
	
	Method ID:String() Property
		Return id
	End
	
	Method GetString:String(prop:String, fallback:String = "")
		Local value:= data.Get(prop)
		If Not value
			Return fallback
		End
		Return value
	End
	
	Method GetInt:Int(prop:String, fallback:Int = 0)
		If Not data.Contains(prop)
			Return fallback
		End
		Return Int(data.Get(prop))
	End
	
	Method GetFloat:Float(prop:String, fallback:Float = 0.0)
		If Not data.Contains(prop)
			Return fallback
		End
		Return Float(data.Get(prop))
	End
	
	Method GetBool:Bool(prop:String, fallback:Bool = False)
		If Not data.Contains(prop)
			Return fallback
		End
		Local result:String = data.Get(prop)
		result = result.ToLower()
		Select result
			Case "0", "no", "false", "off"
				Return False
			Case "1", "yes", "true", "on"
				Return True
		End
		Return fallback
	End
	
	Private
	Field data:StringMap<String>
	Field id:String
End


Usage is like this
Local ini:= New IniFile("path.ini")

Local globals:= ini.GetGlobals()
Print globals.GetInt("width")  'prints 100
Print globals.GetString("description") 'prints Hello World

Local colors:= ini.GetSection("ColorSection")
Print colors.GetFloat("red")  'prints 1.0
Print colors.GetFloat("green")  'prints 0.5
Print colors.GetFloat("blue")  'prints 0.2
Print colors.GetFloat("yellow", 0.8)  'prints 0.8 (fallback) because yelllow was not found


assuming an ini file like this

# This is a comment, values below are in the global section
width = 100
description = Hello World

[ ColorSection ]
red = 1.0
green = 0.5
blue = 0.2



-----
edit: added convinience method Get() which will use the global section automatically.
Local ini:= New IniFile("path.ini")
Print ini.Get("width") 'prints 100



hub(Posted 2015) [#3]
Many thanks to share this Shinkiro1