;;
;;=====================================================================================-----
;;
;;FUNCTION IniArray()
;; ReadIniArray() - subfunction for reading array
;; WriteIniArray() - subfunction for writing array
;;
;;ACTION Reads INI file into an array;
;; Writes array to INI format file & reloads the array with fresh data
;; ReadIniArray() reads a Section:Key:Data set from the array
;; WriteIniArray() writes a Section:Key:Data set to the array
;;
;;AUTHOR Glenn Barnas
;;
;;VERSION 1.3 - 2014/06/23 - Fixed issue when data contained equal signs.
;; 1.2 - 2013/04/20 - Read empty section or file, write empty section,
;; WriteIniArray() - fix error if duplicate write of null data Added Options
;; to IniArray for Write to add blank lines between sections.
;; 1.1 - 2013/04/17 - Improve file load time on large files.
;; 1.0 - 2011/08/02
;;
;;SYNTAX IniArray(filename [,array] [,options])
;;
;;PARAMETERS Filename - REQUIRED - String
;; - The name of the INI file to read/write
;;
;; Array - OPTIONAL - Array
;; - The properly formatted array to write
;;
;; Options - OPTIONAL - Integer
;; - A bitwise settings to control output formatting
;; Value Mode Description
;; 1 Write Adds blank lines between Sections when true
;; 2 Write Suppress the array reload on write, useful when no further INI manipulation is planned.
;;
;;REMARKS Reads an entire INI file into an array for processing. There is no
;; limit on the size of the INI file, other than any Kix limits on
;; general file sizes. On read, blank lines and comments (lines that
;; begin with ";" or "#") are ignored. On write, only sections that
;; contain data/value pairs are written, mimicing the action of
;; WriteProfileString(). Similarly, only Keys that have non-null
;; values are written.
;;
;; The secondary functions ReadIniArray() and WriteIniArray() make
;; using the IniArray() UDF as easy to use as ReadProfileString()
;; and WriteProfileString(), simply requiring calls to IniArray to
;; load and then save the INI file. The Read and Write sub-functions
;; use the same syntax as the ProfileString functions but reference
;; the array instead of the INI file.
;;
;; NOTE: The WriteIniArray function returns the updated array. This
;; is the only significant deviation from the WriteProfileString operation.
;;
;; NOTE: During array manipulation, deleted records are set to null
;; and not reused. Similarly, when a new key/data pair is added, deleted
;; array items are not reused. When the array is written to disk, the
;; null records are skipped. When the file is again read, the array will
;; only contain valid data with no empty records.
;; WRITING an array causes a RE-READ operation, cleaning the empty records.
;;
;; NOTE: IMPORTANT - When using WriteIniArray() to create a new Ini-array,
;; you MUST first declare the array via Dim $array[1, 0]. This is not
;; necessary if you read an INI file first.
;;
;;RETURNS Returns a two-dimensional array of two-dimensional arrays. The first
;; element is the section name, the second element is a two-dimensional
;; array consisting of the key/data pairs.
;;
;; Consider the following simple INI file:
;; [COLORS]
;; Apple=Red
;; Lime=Green
;; [TASTES]
;; Apple=sweet
;; Lime=sour
;;
;; The call $aINI = IniArray('inifile.ini) would return an array that
;; contained the following values:
;; $aINI[0,0] contains "COLORS"
;; $aINI[1,0] contains an array of data/value pairs from this section
;; - extract with $aData = $aINI[1,0]
;; $aData[0,0] contains "Apple"; $aData[1,0] contains "Red"
;; $aData[0,1] contains "Lime"; $aData[1,1] contains "Green"
;;
;; $aINI[0,1] contains "TASTES"
;; $aINI[1,1] contains Array of data/value pairs from this section
;; - extract with $aData = $aINI[1,1]
;; $aData[0,0] contains "Apple"; $aData[1,0] contains "Sweet"
;; $aData[0,1] contains "Lime"; $aData[1,1] contains "Sour"
;;
;; ; the following would return "Sweet", just like ReadProfileString
;; $Taste = ReadIniArray($aINI, 'TASTES', 'Apple')
;;
;;
;;DEPENDENCIES none
;;
;;TESTED WITH W2K, WXP, W2K3, W2K8, W7
;;
;;EXAMPLES INI FILE READ:
;; $aIni = IniArray('testfile.ini')
;; 'File contains ' UBound($aIni, 2) + 1 ' sections' ?
;;
;; ENUMERATE:
;; For $I = 0 to UBound($aIni, 2)
;; $aData = $aIni[1, $I]
;;
;; 'Section: ' $aIni[0, $I] ' contains ' UBound($aData, 2) + 1 ' Data/Value elements' ?
;; ' Press a key: ' get $ ?
;;
;; For $J = 0 to UBound($aData, 2)
;; $J ': ' $aData[0, $J] ' = ' $aData[1, $J] ?
;; Next
;; Next
;;
;; ELEMENT READ:
;; ; Return a specific value
;; $Value = ReadIniArray($aIni, 'SectionName', 'keyname')
;; ; Return an array of all section names
;; $aSections = Split(ReadIniArray($aIni), Chr(10))
;; ; Return an array of Keys in a specific section
;; $aKeys = Split(ReadIniArray($aIni, 'SectionName'), Chr(10))
;;
;; ELEMENT WRITE/UPDATE:
;; ; Write a key/value pair in the named section
;; $aIni = WriteIniArray($aIni, 'SectionName', 'keyname', 'Data')
;;
;; ELEMENT DELETE:
;; ; Remove the named key from the defined section
;; $aIni = WriteIniArray($aIni, 'SectionName', 'keyname')
;;
;; SECTION DELETE:
;; ; Remove all key/value pairs from the section, deleting the section
;; $aIni = WriteIniArray($aIni, 'SectionName')
;;
;; INI FILE WRITE:
;; ; Flush the array to the file and then reload the array
;; $aIni = IniArray('testfile.ini', $aIni)
;
Function IniArray($_fSrcFile, OPTIONAL $_aDataWrite, OPTIONAL $_Options)
Dim $_ ; temp var
Dim $_Fp ; file pointer
Dim $_C, $_I, $_J, $K ; index pointers
Dim $_AddLine, $_NoRead ; Option Vars
Dim $_Sect ; Section Name
Dim $_aDat ; Data pair array
Dim $_aSect[1, 499], $_aData[1, 499] ; Section & Data Arrays
$_NoRead = 0 ; perform read after write
If $_Options
If $_Options & 1
$_AddLine = @CRLF
EndIf
If $_Options & 2
$_NoRead = 1 ; write & exit w/o re-reading
EndIf
EndIf
; Obtain a File Handle for Read or Write operations
; ============================================================
$_Fp = FreeFileHandle ; locate an available file handle
If Not $_Fp
Exit 1 ; none available - exit
EndIf
; WRITE: verify that we have properly formatted data
; ============================================================
If VarType($_aDataWrite) > 0 ; Write the array to the INI file and exit
If VarType($_aDataWrite) < 8192 ; data but not an array - exit!
Exit 87
EndIf
Del $_fSrcFile ; delete any pre-existing file
$_ = Open($_Fp, $_fSrcFile, 5) ; open the file for write/create
If @ERROR Exit @ERROR EndIf ; exit if error opening
; Write the section data. If no data exists in the section, do not write anything!
For $_I = 0 to UBound($_aDataWrite, 2)
$_Sect = $_aDataWrite[0, $_I] ; Section name to write
$_aData = $_aDataWrite[1, $_I] ; Data array
If UBound($_aData, 2) > -1 ; create Sect and write data if data is present
$_ = '[' + $_Sect + ']' + @CRLF ; section name
$_C = 0
For $_J = 0 to UBound($_aData, 2) ; key/data pairs
If $_aData[1, $_J] ; only write keys that have non-null data
$_C = 1
$_ = $_ + $_aData[0, $_J] + '=' + $_aData[1, $_J] + @CRLF
EndIf
Next
If $_C
$_ = WriteLine($_Fp, $_ + $_AddLine) ; write the output line
EndIf
If @ERROR
$_ = Close($_Fp) ; close the file
Exit @ERROR
EndIf ; exit if error writing
EndIf
Next
$_ = Close($_Fp) ; close the file
ReDim $_aData[1, 0] ; clear array
If $_NoRead
exit 0 ; exit here without a re-read of the freshly written data
EndIf
EndIf
; READ: Load the ini file into an array
; ============================================================
$_I = -1 $_J = -1 ; Initialize index pointers
$_ = Open($_Fp, $_fSrcFile, 2) ; open the file for read
If @ERROR
ReDim $_aSect[1, 0]
ReDim $_aData[1, 0]
$_aSect[1, 0] = $_aData
$IniArray = $_aSect ; return an empty array
Exit @ERROR ; exit if error opening
EndIf
ReDim $_aSect[1, 499], $_aData[1, 499] ; Prep Section & Data Arrays for Read
$_ = Trim(ReadLine($_Fp)) ; remove leading/trailing spaces
While Not @ERROR ; loop through the file contents
If Left($_, 1) = '[' ; found a section
If $_I >= 0 ; process prior section data, if any
If $_J >= 0
ReDim Preserve $_aData[1, $_J] ; trim data array to size
$_aSect[1, $_I] = $_aData
ReDim $_aData[1, 499] ; create the
$_J = -1
Else
$_I = $_I - 1 ; ignore empty sections
EndIf
EndIf
$_I = $_I + 1 ; increment the section index
If $_I Mod 499 = 0 And $_I > 0
ReDim Preserve $_aSect[1, $_I + 500]
EndIf
$_aSect[0, $_I] = Split(SubStr($_, 2), ']')[0]
Else
If Not InStr(';#', Left($_, 1)) And Len($_) > 2
$_aDat = Split($_, '=') ; break into array
If UBound($_aDat) > 1 ; does data have embedded = signs?
$_ = $_aDat[1]
For $K = 2 to UBound($_aDat) ; extract them into a single value
$_ = $_ + '=' + $_aDat[$K]
Next
$_aDat[1] = $_
ReDim Preserve $_aDat[1]
EndIf
$_J = $_J + 1 ; increment the data index
If $_J Mod 499 = 0 And $_J > 0
ReDim Preserve $_aData[1, $_J + 500]
EndIf
$_aData[0, $_J] = $_aDat[0] ; Store the value
$_aData[1, $_J] = $_aDat[1] ; Store the data
EndIf
EndIf
$_ = Trim(ReadLine($_Fp)) ; remove leading/trailing spaces
Loop ; done with input data
$_ = Close($_Fp) ; close the file
$_ = 2 ; prep for Not Found exit
; process the last/only section
If $_I >= 0
If $_J >= 0
ReDim Preserve $_aData[1, $_J] ; trim data array to size
$_aSect[1, $_I] = $_aData
Else
ReDim Preserve $_aData[1, 0]
$_aSect[1, $_I] = $_aData
EndIf
ReDim Preserve $_aSect[1, $_I] ; trim section array to size
$_ = 0
EndIf
$IniArray = $_aSect ; return the array
Exit $_ ; exit success
EndFunction
Function ReadIniArray($_aIniFile, OPTIONAL $_Section, OPTIONAL $_Key)
; exit immediately if the data format is invalid
If VarType($_aIniFile) < 8192 ; data but not an array - exit!
Exit 87
EndIf
Dim $_aSectIdx[UBound($_aIniFile, 2)] ; Section Index Array
Dim $_I, $_J ; Index pointers
Dim $_aData ; Array of Key/Data pairs
; Create a section index array
For $_I = 0 to UBound($_aIniFile, 2)
$_aSectIdx[$_I] = $_aIniFile[0, $_I]
Next
; If the Section is null, return a delimited string of Sections [same as ReadProfileString(file)]
If Not $_Section
$ReadIniArray = Join($_aSectIdx, Chr(10))
Exit 0
EndIf
; Search the index for a section
$_I = aScan($_aSectIdx, $_Section)
If $_I < 0 Exit 2 EndIf ; section not found - Exit
$_aData = $_aIniFile[1, $_I] ; Extract the key/value array
; Create a Key index for the requested section
Dim $_aKeyIdx[UBound($_aData, 2)]
For $_J = 0 to UBound($_aData, 2)
$_aKeyIdx[$_J] = $_aData[0, $_J]
Next
; If the Key is null, return a delimited string of Keys [same as ReadProfileString(file, section)]
If Not $_Key
$ReadIniArray = Join($_aKeyIdx, Chr(10))
Exit 0
EndIf
; Search the index for a Key
$_J = aScan($_aKeyIdx, $_Key)
If $_J < 0 Exit 2 EndIf ; Key not found
$ReadIniArray = $_aData[1, $_J]
Exit 0
EndFunction
Function WriteIniArray($_aIniFile, $_Section, OPTIONAL $_Key, OPTIONAL $_Data)
; exit immediately if the data format is invalid
If VarType($_aIniFile) < 8192 ; data but not an array - exit!
Exit 87
EndIf
Dim $_aSectIdx[UBound($_aIniFile, 2)] ; Section Index Array
Dim $_I, $_J ; Index pointers
Dim $_aData ; Key/Data array
; Create a section index array
For $_I = 0 to UBound($_aIniFile, 2)
$_aSectIdx[$_I] = $_aIniFile[0, $_I]
Next
; Search the index for a section
$_I = aScan($_aSectIdx, $_Section)
; If the section does not exist and Keydata is present - add the section and key/value pair (new Sect:Key:Data)
If $_I < 0 ; section not found - Add if Data is present
If $_Key And $_Data
$_I = 0 ; default to empty array
If $_aIniFile[0, 0]
$_I = UBound($_aIniFile, 2) + 1 ; find next section index
EndIf
ReDim Preserve $_aIniFile[1, $_I] ; Add new section
ReDim $_aData[1, 0] ; create data array
$_aData[0,0] = $_Key ; key
$_aData[1,0] = $_Data ; data
$_aIniFile[0, $_I] = $_Section ; section name
$_aIniFile[1, $_I] = $_aData ; section data
Else
Exit 0 ; nothing to do
EndIf
Else ; the section does exist, locate the Key
; If the Key is null, delete the section (delete Section)
If Not $_Key
ReDim $_aData[1, 0] ; create empty keys array
$_aIniFile[1, $_I] = $_aData ; write to section
$_aIniFile[0, $_I] = '' ; write null section name
Else
$_aData = $_aIniFile[1, $_I] ; Extract the key/value array
; Create a Key index for the requested section
Dim $_aKeyIdx[UBound($_aData, 2)]
For $_J = 0 to UBound($_aData, 2)
$_aKeyIdx[$_J] = $_aData[0, $_J] ; create the index
Next
$_J = aScan($_aKeyIdx, $_Key) ; find the key
; If the key does not exist, add the key/data (new Key/data)
If $_J < 0
If $_Data
$_aData = $_aIniFile[1, $_I] ; array of key/data pairs
$_J = UBound($_aData, 2) + 1 ; find next key index
ReDim Preserve $_aData[1, $_J] ; create data array
$_aData[0, $_J] = $_Key ; key
$_aData[1, $_J] = $_Data ; data
$_aIniFile[1, $_I] = $_aData ; section data
Else
$WriteIniArray = $_aIniFile ; return original array
Exit 0 ; nothing to do (no data)
EndIf
Else ; the key exists - either Update or Delete
; if the data is not null, write the new data (update key)
If $_Data
$_aData[1, $_J] = $_Data
; If the data is null, write empty key and data values (delete key)
Else
$_aData[0, $_J] = '' ; delete key
$_aData[1, $_J] = '' ; clear data value
EndIf
$_aIniFile[1, $_I] = $_aData ; update the array
EndIf
EndIf
EndIf
$WriteIniArray = $_aIniFile
Exit 0
EndFunction