Ini file library
Monkey Forums/Monkey Beginners/Ini file library| 
 | ||
| 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. | 
| 
 | ||
| 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
 | 
| 
 | ||
| Many thanks to share this Shinkiro1 |