Page 2 of 2 <12
Topic Options
#207178 - 2013-04-23 10:17 PM Re: Just Sharing - Adding Printers based on computer name via INI [Re: Glenn Barnas]
ChristopheM Offline
Hey THIS is FUN
*****

Registered: 2002-05-13
Posts: 304
Loc: STRASBOURG, France
happy to see that we have similar idea
;\)
_________________________
Christophe

Top
#207179 - 2013-04-24 01:11 AM Re: Just Sharing - Adding Printers based on computer name via INI [Re: ChristopheM]
Glenn Barnas Administrator Offline
KiX Supporter
*****

Registered: 2003-01-28
Posts: 4285
Loc: New Jersey
\:D I put a lot of effort into the quality and performance of our login script - one of the reasons it's used at so many global companies and educational facilities. In a recent deployment at a financial company, we brought the Citrix logon script time down from nearly 30 seconds to under 4.5, and the less complex desktop login script to under 2 seconds.

While many/most of the UDFs it uses are published on our web site, I don't always put those that provide us with a strong competetive edge into the public domain right away! ;\)

Glenn
_________________________
Actually I am a Rocket Scientist! \:D

Top
#207180 - 2013-04-24 03:51 AM Re: Just Sharing - Adding Printers based on computer name via INI [Re: Glenn Barnas]
Lonkero Administrator Offline
KiX Master Guru
*****

Registered: 2001-06-05
Posts: 22333
Loc: OK
so... what you are saying is, that you knew this one was actually slow and maybe even slower than readprofilestring() but did not say it that way, as it gave you competitive edge?

I do have to say that sounds... wacky...
_________________________
!

download KiXnet

Top
#207191 - 2013-04-24 06:43 PM Re: Just Sharing - Adding Printers based on computer name via INI [Re: Lonkero]
Glenn Barnas Administrator Offline
KiX Supporter
*****

Registered: 2003-01-28
Posts: 4285
Loc: New Jersey
The purpose of the IniArray UDF was not an alternative to Read/WriteProfileString, but specifically to overcome the size limitations of INI files. I wrote this for Shane some time ago and Allen's been using it as well.

In my testing, the moderately sized INI files used by my login script saw performance gains over repetetive ReadProfileString calls. I developed another version of this that uses Global arrays that is quite a bit faster, but I haven't published it because - while it works for my login script, it isn't very generic. And - since my login script is a closed-source commercial product, of course I don't publish every one of the UDFs it uses!

I am working on a generic version that uses global arrays, but that will alter the syntax of the read/write calls (eliminating the data source name) and will require careful management of the INI array if multiple data sources are needed. The published version does not have the limitation of a single, globally declared data source.

Glenn
_________________________
Actually I am a Rocket Scientist! \:D

Top
#207192 - 2013-04-24 09:18 PM Re: Just Sharing - Adding Printers based on computer name via INI [Re: Glenn Barnas]
Glenn Barnas Administrator Offline
KiX Supporter
*****

Registered: 2003-01-28
Posts: 4285
Loc: New Jersey
OK - I'm starting to wonder about the performance of arrays in Kix...

I modified the IniArray library to use a single, global array. No more passing arrays to/from the functions. While I can determine that this is slightly faster than passing the arrays, it is (surprisingly) nowhere near the direct read performance of ReadProfileString.

Taking your test code and my new function with Global arrays, I get the following results. The one difference in my code is that I separated out the array load time (LTime) from the ReadIniArray enumerations.
 Code:
    size   LTime   time1   time2  filename
    1199      16       0      16  .\test1.ini
    7131      31     141      62  .\test2.ini
   19989      47     687     188  .\test3.ini
   63231     141    4375     890  .\test4.ini
   91229     219   12062    1703  .\test5.ini
This is on an older P4 3.2GHz w/ Win-8. On my Win-7 test system, I get
 Code:
    size   LTime   time1   time2  filename
    1199       0      16       0  .\test1.ini
    7131      16     141      78  .\test2.ini
   19989      16     391     187  .\test3.ini
   63231      78    2484    1032  .\test4.ini
   91229     109    7125    2109  .\test5.ini
All testing was done on local storage - 72K RPM SATA. Interestingly, running over the network showed better performance using IniArray.
Win-8 PC on Network drive (100Mbps)
 Code:
    size   LTime   time1   time2  filename
    1199       0      16      47  .\test1.ini
    7131      31     140     438  .\test2.ini
   19989      62     688    2156  .\test3.ini
   63231     156    4360   16625  .\test4.ini
   91229     250   12109   34672  .\test5.ini
Win-7 Test PC on Network drive(Gig-E)
 Code:
    size   LTime   time1   time2  filename
    1199      16       0      47  .\test1.ini
    7131      15     141     328  .\test2.ini
   19989      47     562    1000  .\test3.ini
   63231     140    2704    4875  .\test4.ini
   91229     203    7047    8985  .\test5.ini
Just thought this was interesting.

I think I'll drop the Global Array model for now since the performance benefit is slight. The IniArray library has benefit for network based I/O and for larger files.

Glenn

Here's the modified test code and global array version of IniArray:
 Code:
Break On

Dim $

$ = SetOption('Explicit', 'On')
$ = SetOption('WrapAtEOL', 'On')
$ = SetOption('NoVarsInStrings', 'On')

If Not Exist('.\Test1.ini')
  'Generating test files' 
  GenIni('.\Test1.ini', 10, 5)
  '!'
  GenIni('.\Test2.ini', 50, 8)
  '!'
  GenIni('.\Test3.ini', 100, 15)
  '!'
  GenIni('.\Test4.ini', 250, 18)
  '!'
  GenIni('.\Test5.ini', 500, 12)
  'Done!' ? ?
EndIf
  
'Press a key to continue ' Get $ ?

dim $arrInifiles, $inifile, $tmpfile, $ini, $arrSections, $arrKeys, $section, $key, $d0, $d1, $d2, $Size, $Start, $Value

$arrInifiles =
  ".\test1.ini",
  ".\test2.ini",
  ".\test3.ini",
  ".\test4.ini",
  ".\test5.ini"

right( "        size",8 )
right( "        LTime",8 )
right( "        time1",8 )
right( "        time2",8 )
"  filename"
?
for each $inifile in $arrInifiles
  if $inifile
    $tmpfile = @scriptdir+"\"+@ScriptName+".tmp"
    COPY $inifile $tmpfile

    $size = GetFileSize($tmpfile)

    ;-- read with ini functions --
    $start = @TICKS
    $ini = IniArray($tmpfile)
    $d0 = @TICKS - $Start
    $start = @TICKS
    $arrSections = Split(ReadIniArray(''), Chr(10))
    for each $section in $arrSections
      $arrKeys = Split(ReadIniArray($section), Chr(10))
      for each $key in $arrKeys
        $value = ReadIniArray($section, $key )
      next
    next
    $d1 = @TICKS - $start

    ;-- read with ReadProfileString --
    $start = @TICKS
    $arrSections = ReadProfileString( $tmpfile, "", "" )
    $arrSections = split($arrSections, chr(10) )
    for each $section in $arrSections
      if $section
        $arrKeys = ReadProfileString( $tmpfile, $section, "" )
        $arrKeys = split($arrKeys, chr(10) )
        for each $key in $arrKeys
          if $key
            $value = ReadProfileString( $tmpfile, $section, $key )
          endif
        next
      endif
    next
    $d2 = @TICKS - $start

    right( "        "+$size,8 )
    right( "        "+$d0,8 )
    right( "        "+$d1,8 )
    right( "        "+$d2,8 )
    "  "
    $inifile
    ?

    DEL $tmpfile
  endif
next


Function GenIni($_File, $_S, $_MKey)

  Dim $_, $_X, $_Y
  Dim $_Section, $_Key, $_Data

  Del $_File

  SRnd(@MSECS)
  For $_X = 1 to $_S				; Sections
    For $_Y = 1 to 1 + Rnd($_MKey)		; Keys
      $_Section = 'SECTION_' + Right('0000' + $_X, 4)
      $_Key = 'Key_' + Right('00' + $_Y, 2)
      $_Data = Left('The quick brown fox jumped over the lazy dog.', Rnd(30))
      $_ = WriteProfileString($_File, $_Section, $_Key, $_Data)
    Next
    If $_X Mod 50 = 0 '.' EndIf
  Next

  Exit 0

EndFunction






;;
;;======================================================================
;;
;;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.0 / 2011-08-02
;;		 1.1 / 2013-04-17 - improve file load on large files
;;		 1.2 / 2013-04-20 - bugfixes:
;;				    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
;;
;;SYNTAX         IniArray(filename, Action [,options])
;;
;;PARAMETERS     filename - REQUIRED - the name of the INI file to read/write
;;
;;		 action	  - OPTIONAL - "R" or "W" for Read or Write - Defaults to Read
;;
;;		 options  - OPTIONAL - 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 global array called $a_INIDATA_ is used for operations and will be declared
;;		 by the first call to IniArray if it was not previously defined.
;;		 
;;		 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: 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 $a_INIARRAY_[1, 0]. This is not necessary
;;		       if you read an INI file first with IniArray(filename).
;; 
;;RETURNS        IniArray: Returns 1 on success or 0 on failure
;;		 Populates 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.
;;
;;		 ReadIniArray: returns the data specified by the section/value pair
;;		 WriteIniArray: Returns 1 on success or 0 on failure
;;		 Updates the global array with the section/value/data provided
;;
;;		 Consider the following simple INI file:
;;		  [COLORS]
;;		  Apple=Red
;;		  Lime=Green
;;		  [TASTES]
;;		  Apple=sweet
;;		  Lime=sour
;;
;;		 The call $Rc = IniArray('inifile.ini') would populate the $a_INIDATA_ array
;;		 that contained the following values:
;;		  $a_INIDATA_[0,0] contains "COLORS"
;;		  $a_INIDATA_[1,0] contains an array of data/value pairs from this section
;;		  - extract with $aData = $a_INIDATA_[1,0]
;;		   $aData[0,0] contains "Apple"; $aData[1,0] contains "Red"
;;		   $aData[0,1] contains "Lime"; $aData[1,1] contains "Green"
;;
;;		  $a_INIDATA_[0,1] contains "TASTES"
;;		  $a_INIDATA_[1,1] contains Array of data/value pairs from this section
;;		  - extract with $aData = $a_INIDATA_[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('TASTES', 'Apple')
;;
;;
;;DEPENDENCIES   none
;;
;;TESTED WITH    W2K, WXP, W2K3, W2K8, W7
;;
;;EXAMPLES       INI FILE READ:
;;		   $Rc = IniArray('testfile.ini')
;;		   'File contains ' UBound($a_INIDATA_) + 1 ' sections' ?
;;
;;		 ENUMERATE:
;;		   For $I = 0 to UBound($a_INIDATA_, 2)
;;		     $aData = $a_INIDATA_[1, $I]
;;		  
;;		     'Section: ' $a_INIDATA_[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('SectionName', 'keyname')
;;		   ; Return an array of all section names
;;		   $aSections = Split(ReadIniArray(''), Chr(10))
;;		   ; Return an array of Keys in a specific section
;;		   $aKeys = Split(ReadIniArray('SectionName'), Chr(10))
;;
;;		  ELEMENT WRITE/UPDATE:
;;		   ; Write a key/value pair in the named section
;;		   $Rc = WriteIniArray('SectionName', 'keyname', 'Data')
;;
;;		  ELEMENT DELETE:
;;		   ; Remove the named key from the defined section
;;		   $Rc = WriteIniArray('SectionName', 'keyname')
;;
;;		  SECTION DELETE:
;;		   ; Remove all key/value pairs from the section, deleting the section
;;		   $Rc = WriteIniArray('SectionName')
;;
;;		  INI FILE WRITE:
;;		   ; Flush the array to the file and then reload the array
;;		   $Rc = IniArray('testfile.ini', "W")
;
Function IniArray($_fSrcFile, OPTIONAL $_DataWrite, OPTIONAL $_Options)

  Dim $_					; temp var
  Dim $_Fp					; file pointer
  Dim $_C, $_I, $_J				; index pointers
  Dim $_AddLine, $_NoRead			; Option Vars
  Dim $_Sect					; Section Name
  Dim $_aDat					; Data pair array
  Dim $_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 $_DataWrite = 'W'				; Write operation
    If VarType($a_INIDATA_) < 8192		; 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
      ReDim $a_INIDATA_[1, 0]			; create empty array
      Exit @ERROR				; exit if error opening
    EndIf

    ; Write the section data. If no data exists in the section, do not write anything!
    For $_I = 0 to UBound($a_INIDATA_, 2)
      $_Sect  = $a_INIDATA_[0, $_I]		; Section name to write
      $_aData = $a_INIDATA_[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

  ; declare the INI array if undefined
  If Not IsDeclared($a_INIDATA_)
    Global $a_INIDATA_[1,0]
  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 $a_INIDATA_[1, 0]			; return an empty array
    Exit @ERROR					; exit if error opening
  EndIf

  ReDim $a_INIDATA_[1, 499], $_aData[1, 499]	; Prep Section & Data Arrays for Read

  $_ = Trim(ReadLine($_Fp))			; read INI, removing 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
          $a_INIDATA_[1, $_I] = $_aData
          ReDim $_aData[1, 499]			; create the data array for the new section
          $_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 $a_INIDATA_[1, $_I + 500] ; increase the section input buffer
      EndIf
      $a_INIDATA_[0, $_I] = Split(SubStr($_, 2), ']')[0]
    Else
      If Not InStr(';#', Left($_, 1)) And Len($_) > 2
        $_aDat = Split($_, '=')			; break into array
        $_J = $_J + 1				; increment the data index
        If $_J Mod 499 = 0 And $_J > 0
          ReDim Preserve $_aData[1, $_J + 500]	; increase the key input buffer
        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
      $a_INIDATA_[1, $_I] = $_aData
    Else
      ReDim Preserve $_aData[1, 0]
      $a_INIDATA_[1, $_I] = $_aData
    EndIf
    ReDim Preserve $a_INIDATA_[1, $_I]		; trim section array to size
    $_ = 0
  EndIf

  Exit $_					; exit success

EndFunction


Function ReadIniArray(OPTIONAL $_Section, OPTIONAL $_Key)

  Dim $_I, $_J					; Index pointers
  Dim $_aData, $_aSrc				; Array of Key/Data pairs
  Dim $_Cmd
  Dim $_S
  Dim $_

  ; exit immediately if the data format is invalid
  If VarType($a_INIDATA_) < 8192					; data but not an array - exit!
    Exit 87
  EndIf

  ; Get the array size
  $_S = UBound($a_INIDATA_, 2)
  If $_S <= 0
    Exit 87
  EndIf

  Dim $_aSectIdx[$_S]				; Section Index Array

  ; Create a section index array
  For $_I = 0 to $_S
    $_aSectIdx[$_I] = $a_INIDATA_[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 = $a_INIDATA_[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($_Section, OPTIONAL $_Key, OPTIONAL $_Data)

  ; exit immediately if the data format is invalid
  If VarType($a_INIDATA_) < 8192		; data but not an array - exit!
    Exit 87
  EndIf

  Dim $_aSectIdx[UBound($a_INIDATA_, 2)]	; Section Index Array
  Dim $_I, $_J					; Index pointers
  Dim $_aData					; Key/Data array


  ; Create a section index array
  For $_I = 0 to UBound($a_INIDATA_, 2)
    $_aSectIdx[$_I] = $a_INIDATA_[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 $a_INIDATA_[0, 0]
        $_I = UBound($a_INIDATA_, 2) + 1		; find next section index
      EndIf
      ReDim Preserve $a_INIDATA_[1, $_I]		; Add new section
      ReDim $_aData[1, 0]			; create data array
      $_aData[0,0] = $_Key			; key
      $_aData[1,0] = $_Data			; data
      $a_INIDATA_[0, $_I] = $_Section		; section name
      $a_INIDATA_[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
      $a_INIDATA_[1, $_I] = $_aData		; write to section
      $a_INIDATA_[0, $_I] = ''			; write null section name
    Else
      $_aData = $a_INIDATA_[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 = $a_INIDATA_[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
          $a_INIDATA_[1, $_I] = $_aData		; section data
        Else
          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
        $a_INIDATA_[1, $_I] = $_aData		; update the array
      EndIf

    EndIf

  EndIf

  Exit 0

EndFunction
_________________________
Actually I am a Rocket Scientist! \:D

Top
#207198 - 2013-04-25 02:15 AM Re: Just Sharing - Adding Printers based on computer name via INI [Re: Glenn Barnas]
Lonkero Administrator Offline
KiX Master Guru
*****

Registered: 2001-06-05
Posts: 22333
Loc: OK
that is indeed weird.
was thinking about checking the readiniarray() syntax for obvious time hogs, but the UDF is not in our library.
_________________________
!

download KiXnet

Top
#207199 - 2013-04-25 02:24 AM Re: Just Sharing - Adding Printers based on computer name via INI [Re: Lonkero]
Lonkero Administrator Offline
KiX Master Guru
*****

Registered: 2001-06-05
Posts: 22333
Loc: OK
found it.

and now found it in your script too \:D


Edited by Lonkero (2013-04-25 02:25 AM)
_________________________
!

download KiXnet

Top
#207200 - 2013-04-25 04:28 AM Re: Just Sharing - Adding Printers based on computer name via INI [Re: Lonkero]
Lonkero Administrator Offline
KiX Master Guru
*****

Registered: 2001-06-05
Posts: 22333
Loc: OK
hmm... I will try a different design if I don't fall asleep too fast.
_________________________
!

download KiXnet

Top
#207201 - 2013-04-25 06:18 AM Re: Just Sharing - Adding Printers based on computer name via INI [Re: Lonkero]
Lonkero Administrator Offline
KiX Master Guru
*****

Registered: 2001-06-05
Posts: 22333
Loc: OK
did try couple different ones... it's still just as slow.
I eliminated the multidim array, in hopes of some improvement. did a separate index array for the keys... still not improving. I am starting to think it might not actually have anything to do with arrays but UDF processing itself.

here is my tweaked one...
 Code:
Break On

Dim $

$ = SetOption('Explicit', 'On')
$ = SetOption('WrapAtEOL', 'On')
$ = SetOption('NoVarsInStrings', 'On')

If Not Exist('.\Test1.ini')
  'Generating test files' 
  GenIni('.\Test1.ini', 10, 5)
  '!'
  GenIni('.\Test2.ini', 50, 8)
  '!'
  GenIni('.\Test3.ini', 100, 15)
  '!'
  GenIni('.\Test4.ini', 250, 18)
  '!'
  GenIni('.\Test5.ini', 500, 12)
  'Done!' ? ?
EndIf
  
'Press a key to continue ' Get $ ?

dim $arrInifiles, $inifile, $tmpfile, $ini, $arrSections, $arrKeys, $section, $key, $d0, $d1, $d2, $Size, $Start, $Value

$arrInifiles =
  ".\test1.ini",
  ".\test2.ini",
  ".\test3.ini",
  ".\test4.ini",
  ".\test5.ini"

right( "        size",8 )
right( "        LTime",8 )
right( "        time1",8 )
right( "        time2",8 )
"  filename"
?
for each $inifile in $arrInifiles
  if $inifile
    $tmpfile = @scriptdir+"\"+@ScriptName+".tmp"
    COPY $inifile $tmpfile

    $size = GetFileSize($tmpfile)


    ;-- read with ini functions --
    $start = @TICKS
    $ini = IniArray($tmpfile)
    $d0 = @TICKS - $Start
    $start = @TICKS
    $arrSections = Split(ReadIniArray(''), Chr(10))
    for each $section in $arrSections
      $arrKeys = Split(ReadIniArray($section), Chr(10))
      for each $key in $arrKeys
        $value = ReadIniArray($section, $key )
      next
    next
    $d1 = @TICKS - $start

    ;-- read with ReadProfileString --
    $start = @TICKS
    $arrSections = ReadProfileString( $tmpfile, "", "" )
    $arrSections = split($arrSections, chr(10) )
    for each $section in $arrSections
      if $section
        $arrKeys = ReadProfileString( $tmpfile, $section, "" )
        $arrKeys = split($arrKeys, chr(10) )
        for each $key in $arrKeys
          if $key
            $value = ReadProfileString( $tmpfile, $section, $key )
          endif
        next
      endif
    next
    $d2 = @TICKS - $start

    right( "        "+$size,8 )
    right( "        "+$d0,8 )
    right( "        "+$d1,8 )
    right( "        "+$d2,8 )
    "  "
    $inifile
    ?

    DEL $tmpfile
  endif
next

? "finished" ?

get $

Function GenIni($_File, $_S, $_MKey)

  Dim $_, $_X, $_Y
  Dim $_Section, $_Key, $_Data

  Del $_File

  SRnd(@MSECS)
  For $_X = 1 to $_S				; Sections
    For $_Y = 1 to 1 + Rnd($_MKey)		; Keys
      $_Section = 'SECTION_' + Right('0000' + $_X, 4)
      $_Key = 'Key_' + Right('00' + $_Y, 2)
      $_Data = Left('The quick brown fox jumped over the lazy dog.', Rnd(30))
      $_ = WriteProfileString($_File, $_Section, $_Key, $_Data)
    Next
    If $_X Mod 50 = 0 '.' EndIf
  Next

  Exit 0

EndFunction






;;
;;======================================================================
;;
;;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.0 / 2011-08-02
;;		 1.1 / 2013-04-17 - improve file load on large files
;;		 1.2 / 2013-04-20 - bugfixes:
;;				    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
;;
;;SYNTAX         IniArray(filename, Action [,options])
;;
;;PARAMETERS     filename - REQUIRED - the name of the INI file to read/write
;;
;;		 action	  - OPTIONAL - "R" or "W" for Read or Write - Defaults to Read
;;
;;		 options  - OPTIONAL - 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 global array called $a_INIDATA_ is used for operations and will be declared
;;		 by the first call to IniArray if it was not previously defined.
;;		 
;;		 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: 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 $a_INIARRAY_[1, 0]. This is not necessary
;;		       if you read an INI file first with IniArray(filename).
;; 
;;RETURNS        IniArray: Returns 1 on success or 0 on failure
;;		 Populates 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.
;;
;;		 ReadIniArray: returns the data specified by the section/value pair
;;		 WriteIniArray: Returns 1 on success or 0 on failure
;;		 Updates the global array with the section/value/data provided
;;
;;		 Consider the following simple INI file:
;;		  [COLORS]
;;		  Apple=Red
;;		  Lime=Green
;;		  [TASTES]
;;		  Apple=sweet
;;		  Lime=sour
;;
;;		 The call $Rc = IniArray('inifile.ini') would populate the $a_INIDATA_ array
;;		 that contained the following values:
;;		  $a_INIDATA_[0,0] contains "COLORS"
;;		  $a_INIDATA_[1,0] contains an array of data/value pairs from this section
;;		  - extract with $aData = $a_INIDATA_[1,0]
;;		   $aData[0,0] contains "Apple"; $aData[1,0] contains "Red"
;;		   $aData[0,1] contains "Lime"; $aData[1,1] contains "Green"
;;
;;		  $a_INIDATA_[0,1] contains "TASTES"
;;		  $a_INIDATA_[1,1] contains Array of data/value pairs from this section
;;		  - extract with $aData = $a_INIDATA_[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('TASTES', 'Apple')
;;
;;
;;DEPENDENCIES   none
;;
;;TESTED WITH    W2K, WXP, W2K3, W2K8, W7
;;
;;EXAMPLES       INI FILE READ:
;;		   $Rc = IniArray('testfile.ini')
;;		   'File contains ' UBound($a_INIDATA_) + 1 ' sections' ?
;;
;;		 ENUMERATE:
;;		   For $I = 0 to UBound($a_INIDATA_, 2)
;;		     $aData = $a_INIDATA_[1, $I]
;;		  
;;		     'Section: ' $a_INIDATA_[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('SectionName', 'keyname')
;;		   ; Return an array of all section names
;;		   $aSections = Split(ReadIniArray(''), Chr(10))
;;		   ; Return an array of Keys in a specific section
;;		   $aKeys = Split(ReadIniArray('SectionName'), Chr(10))
;;
;;		  ELEMENT WRITE/UPDATE:
;;		   ; Write a key/value pair in the named section
;;		   $Rc = WriteIniArray('SectionName', 'keyname', 'Data')
;;
;;		  ELEMENT DELETE:
;;		   ; Remove the named key from the defined section
;;		   $Rc = WriteIniArray('SectionName', 'keyname')
;;
;;		  SECTION DELETE:
;;		   ; Remove all key/value pairs from the section, deleting the section
;;		   $Rc = WriteIniArray('SectionName')
;;
;;		  INI FILE WRITE:
;;		   ; Flush the array to the file and then reload the array
;;		   $Rc = IniArray('testfile.ini', "W")
;
Function IniArray($_fSrcFile, OPTIONAL $_DataWrite, OPTIONAL $_Options)

  Dim $_					; temp var
  Dim $_Fp					; file pointer
  Dim $_C, $_I, $_J				; index pointers
  Dim $_AddLine, $_NoRead			; Option Vars
  Dim $_Sect					; Section Name
  Dim $_aDat					; Data pair array
  Dim $_aData[1, 49]				; 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 $_DataWrite = 'W'				; Write operation
    If VarType($a_INIDATA_) < 8192		; 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
      ReDim $a_INIDATA_[1, 0]			; create empty array
      Exit @ERROR				; exit if error opening
    EndIf

    ; Write the section data. If no data exists in the section, do not write anything!
    For $_I = 0 to UBound($a_INIDATA_, 2)
      $_Sect  = $a_INIDATA_[0, $_I]		; Section name to write
      $_aData = $a_INIDATA_[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

  ; declare the INI array if undefined
  If Not IsDeclared($a_INIDATA_)
    Global $a_INIDATA_[1,0]
  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 $a_INIDATA_[0]			; return an empty array
    Exit @ERROR					; exit if error opening
  EndIf

  ReDim $a_INIDATA_[49], $_aKey[49], $_aData[49]	; Prep Section & Data Arrays for Read

  $_ = Trim(ReadLine($_Fp))			; read INI, removing 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[$_J]	; trim data array to size
          ReDim Preserve $_aKey[$_J]	; trim data array to size
dim $sps[1]
$sps[0]=$_aKey
$sps[1]=$_aData
          $a_INIDATA_[$_I][1] = $sps
          ReDim $_aKey[49]			; create the data array for the new section
          ReDim $_aData[49]			; create the data array for the new section
          $_J = -1
        Else
          $_I = $_I - 1				; ignore empty sections
        EndIf
      EndIf

      $_I = $_I + 1				; increment the section index
      If $_I Mod 49 = 0 And $_I > 0
        ReDim Preserve $a_INIDATA_[$_I + 50] ; increase the section input buffer
      EndIf
dim $indx[1]
$indx[0] = Split(SubStr($_, 2), ']')[0]
      $a_INIDATA_[$_I] = $indx
    Else
      If Not InStr(';#', Left($_, 1)) And Len($_) > 2
        $_aDat = Split($_, '=')			; break into array
        $_J = $_J + 1				; increment the data index
        If $_J Mod 49 = 0 And $_J > 0
          ReDim Preserve $_aData[$_J + 50]	; increase the key input buffer
        EndIf
        $_aKey[$_J] = $_aDat[0]		; Store the value
        $_aData[$_J] = $_aDat[1]		; Store the value
      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 $_aKey[$_J]		; trim data array to size
      ReDim Preserve $_aData[$_J]		; trim data array to size
dim $sps[1]
$sps[0]=$_aKey
$sps[1]=$_aData
      $a_INIDATA_[$_I][1] = $sps
    Else
      ReDim Preserve $_aData[0]
      ReDim Preserve $_aKey[0]		; trim data array to size
dim $sps[1]
$sps[0]=$_aKey
$sps[1]=$_aData
      $a_INIDATA_[$_I][1] = $sps
    EndIf
    ReDim Preserve $a_INIDATA_[$_I]		; trim section array to size
    $_ = 0
  EndIf

  Exit $_					; exit success

EndFunction


Function ReadIniArray(OPTIONAL $_Section, OPTIONAL $_Key)

  Dim $_I, $_J					; Index pointers
  Dim $_aData, $_aSrc				; Array of Key/Data pairs
  Dim $_Cmd
  Dim $_S
  Dim $_

  ; exit immediately if the data format is invalid
  If VarType($a_INIDATA_) < 8192					; data but not an array - exit!
    Exit 87
  EndIf

  ; Get the array size
  $_S = UBound($a_INIDATA_)
  If $_S <= 0
    Exit 87
  EndIf

  Dim $_aSectIdx[$_S]			; Section Index Array

  ; Create a section index array
  For $_I = 0 to $_S
    $_aSectIdx[$_I] = $a_INIDATA_[$_I][0]
  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 = $a_INIDATA_[$_I][1]			; Extract the key/value array

  ; Create a Key index for the requested section
   If Not $_Key
    $readiniarray=join($_aData[0],chr(10))
    exit 0
   endif
   if 0>ascan($_aData[0],$_Key)
    exit 2
   endif
   $ReadIniArray = $_aData[1][ascan($_aData[0],$_Key)]


EndFunction


Function WriteIniArray($_Section, OPTIONAL $_Key, OPTIONAL $_Data)

  ; exit immediately if the data format is invalid
  If VarType($a_INIDATA_) < 8192		; data but not an array - exit!
    Exit 87
  EndIf

  Dim $_aSectIdx[UBound($a_INIDATA_, 2)]	; Section Index Array
  Dim $_I, $_J					; Index pointers
  Dim $_aData					; Key/Data array


  ; Create a section index array
  For $_I = 0 to UBound($a_INIDATA_, 2)
    $_aSectIdx[$_I] = $a_INIDATA_[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 $a_INIDATA_[0, 0]
        $_I = UBound($a_INIDATA_, 2) + 1		; find next section index
      EndIf
      ReDim Preserve $a_INIDATA_[1, $_I]		; Add new section
      ReDim $_aData[1]			; create data array
      $_aData[0] = $_Key			; key
      $_aData[1] = $_Data			; data
      $a_INIDATA_[0, $_I] = $_Section		; section name
      $a_INIDATA_[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]			; create empty keys array
      $a_INIDATA_[1, $_I] = $_aData		; write to section
      $a_INIDATA_[0, $_I] = ''			; write null section name
    Else
      $_aData = $a_INIDATA_[1, $_I]		; Extract the key/value array
      ; Create a Key index for the requested section
      Dim $_aKeyIdx[UBound($_aData)]
      For $_J = 0 to UBound($_aData)
        $_aKeyIdx[$_J] = $_aData[$_J][0]	; 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 = $a_INIDATA_[1, $_I]		; array of key/data pairs
          $_J = UBound($_aData) + 1		; find next key index
          ReDim Preserve $_aData[$_J]	; create data array
dim $dats[1]
$dats[0] = $_Key		; key
          $dats[1] = $_Data		; data
          $_aData[$_J] = $dats
          $a_INIDATA_[1, $_I] = $_aData		; section data
        Else
          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[$_J][1] = $_Data

        ; If the data is null, write empty key and data values (delete key)
        Else
dim $dats[1]
          $_aData[$_J] = $dats			; delete key
        EndIf
        $a_INIDATA_[1, $_I] = $_aData		; update the array
      EndIf

    EndIf

  EndIf

  Exit 0

EndFunction
_________________________
!

download KiXnet

Top
#207202 - 2013-04-25 06:26 AM Re: Just Sharing - Adding Printers based on computer name via INI [Re: Lonkero]
Lonkero Administrator Offline
KiX Master Guru
*****

Registered: 2001-06-05
Posts: 22333
Loc: OK
modified my readiniarray to just be a stub:
 Code:

Function ReadIniArray($file, OPTIONAL $_Section, OPTIONAL $_Key)
 $ReadIniArray = readprofilestring($file,$_Section,$_Key)
EndFunction


and still the processing time hikes in the last one:
 Code:
   91539     312    5523    2074  .\test5.ini

_________________________
!

download KiXnet

Top
#207203 - 2013-04-25 11:22 AM Re: Just Sharing - Adding Printers based on computer name via INI [Re: Lonkero]
ChristopheM Offline
Hey THIS is FUN
*****

Registered: 2002-05-13
Posts: 304
Loc: STRASBOURG, France
Hello,

done a new version of functions with a handle instead of an array (and prefix all functions with CM). the script tests files with 3 methods :
- time1 uses ReadProfileString
- time2 uses my own functions CMIniArray, CMReadIniArray, etc...
- time3 uses IniArray, ReadIniArray, etc...

here is the complete code for test (just create a subdir called "test" with inifiles or change the content of $arrInifiles) :
 Code:
break on

$=setoption( "explicit", "on" )

dim $arrInifiles, $inifile, $tmpfile, $size, $ini, $start, $d1, $d2, $d3
dim $arrSections, $arrKeys, $section, $key, $value

$arrInifiles =
    @scriptdir+"\test\file010.ini",
    @scriptdir+"\test\file020.ini",
    @scriptdir+"\test\file030.ini",
    @scriptdir+"\test\file040.ini",
    @scriptdir+"\test\file050.ini",
    @scriptdir+"\test\file060.ini",
    @scriptdir+"\test\file070.ini",
    @scriptdir+"\test\file080.ini",
    @scriptdir+"\test\file090.ini",
    @scriptdir+"\test\file100.ini",
    ""

right( "        size", 8 )
right( "        time1",8 )
right( "        time2",8 )
right( "        time3",8 )
"  filename"
?
for each $inifile in $arrInifiles
    if $inifile
        $tmpfile = @scriptdir+"\"+@ScriptName+".tmp"

        $size = GetFileSize($inifile)

        ;-- read with ReadProfileString --
        $start = @TICKS
        COPY $inifile $tmpfile
        $arrSections = ReadProfileString( $tmpfile, "", "" )
        $arrSections = split($arrSections, chr(10) )
        for each $section in $arrSections
            if $section
                $arrKeys = ReadProfileString( $tmpfile, $section, "" )
                $arrKeys = split($arrKeys, chr(10) )
                for each $key in $arrKeys
                    if $key
                        $value = ReadProfileString( $tmpfile, $section, $key )
                    endif
                next
            endif
        next
        $d1 = @TICKS - $start

        ;-- read with ini functions (version Christophe) --
        $start = @TICKS
        $ini = CMIniArray( $inifile )
        if not @error
            $arrSections = CMINISections($ini)
            for each $section in $arrSections
                $arrKeys = CMINIKeys( $ini, $section )
                for each $key in $arrKeys
                    $value = CMReadIniArray( $ini, $section, $key )
                next
            next
        endif
        $ = CMCloseIniArrayHandle( $ini )
        $d2 = @TICKS - $start

        $ = CMCloseIniArrayHandle( $ini )

        ;-- read with ini functions (version Glenn) --
        $start = @TICKS
        $ini = IniArray( $inifile )
        if not @error
            $arrSections = INISections($ini)
            for each $section in $arrSections
                $arrKeys = INIKeys( $ini, $section )
                for each $key in $arrKeys
                    $value = ReadIniArray( $ini, $section, $key )
                next
            next
        endif
        $d3 = @TICKS - $start

        right( "        "+$size,8 )
        right( "        "+$d1,  8 )
        right( "        "+$d2,  8 )
        right( "        "+$d3,  8 )
        "  "
        $inifile
        ?

        DEL $tmpfile
    endif
next

;-------------------------------------------------------------------------------
; set of function to read in a ini file (version Christophe)
;-------------------------------------------------------------------------------
; internal function
; returned handle is the index of the entry in a global array
; each item in the array is an array of 4 values :
;   entry 0 is a boolean (handle used or not)
;   entry 1 is the name of the ini file
;   entry 2 is the number of sections found in the ini file
;   entry 3 is an array of sections structures
;
; each section structure is an array of 3 values :
;   entry 0 is the name of the section
;   entry 1 is the number of keys in the section
;   entry 2 is an array of keys structures
;
; each key structure is an array of 2 values :
;   entry 0 is the name of the key
;   entry 1 is the value
;-------------------------------------------------------------------------------
function CMGetIniArrayHandle( $inifilename )
    if not IsDeclared( $_IniArrayGlobalHandle )
        ;-- initialize global internal variables --
        global $_IniArrayGlobalHandleNb            $_IniArrayGlobalHandleNb = -1
        global $_IniArrayGlobalHandleInc        $_IniArrayGlobalHandleInc = 32
        global $_IniArrayGlobalHandle[$_IniArrayGlobalHandleInc]
    endif

    dim $i

    for $i = 0 to $_IniArrayGlobalHandleNb
        if ($_IniArrayGlobalHandle[$i][0]=1) and ($_IniArrayGlobalHandle[$i][1]=$inifilename)
            ;-- inifile already loaded : reuse the same handle --
            $CMGetIniArrayHandle = $i
            exit 0
        endif

        if ($_IniArrayGlobalHandle[$i][0]=0)
            ;-- use a free existing handle --
            $_IniArrayGlobalHandle[$i] = 1, $inifilename, 0, ""
            $CMGetIniArrayHandle = $i
            exit 0
        endif
    next

    ;-- use a new handle --
    $_IniArrayGlobalHandleNb = $_IniArrayGlobalHandleNb + 1
    if $_IniArrayGlobalHandleNb > UBound($_IniArrayGlobalHandle)
        redim preserve $_IniArrayGlobalHandle[ UBound($_IniArrayGlobalHandle)+$_IniArrayGlobalHandleInc ]
    endif
    $_IniArrayGlobalHandle[$_IniArrayGlobalHandleNb] = 1, $inifilename, 0, ""

    $CMGetIniArrayHandle = $_IniArrayGlobalHandleNb
endfunction


;-------------------------------------------------------------------------------
; external function
;-------------------------------------------------------------------------------
function CMCloseIniArrayHandle( $inihandle )
    if $inihandle < 0                           exit    endif
    if $inihandle > $_IniArrayGlobalHandleNb    exit    endif

    if $_IniArrayGlobalHandle[$inihandle][0]=1
        if $_IniArrayGlobalHandle[$inihandle][2]>0
            dim $i
            for $i = 0 to UBound( $_IniArrayGlobalHandle[$inihandle][3] )
                $_IniArrayGlobalHandle[$inihandle][3][$i][1] = 0
                $_IniArrayGlobalHandle[$inihandle][3][$i][2] = ""
            next
        endif
        $_IniArrayGlobalHandle[$inihandle] = 0, "", 0, ""
    endif
endfunction


function CMIniArray( $inifilename )
    dim $fHandle                                         ; file pointer

    $CMIniArray = -1
    $fHandle = FreeFileHandle()                          ; locate an available file handle
    if not $fHandle
        exit 1                                           ; none available - exit
    endif

    ;-- try to open file for read into a memory structure --
    if Open($fHandle, $inifilename, 2)<>0                ; open the file for read
        exit 2                                           ; error opening - exit
    endif

    dim $inihandle
    $inihandle = CMGetIniArrayHandle( $inifilename )

    dim $inc        $inc = 128
    dim $arrSections[$inc], $nbSection, $section
    dim $arrKeys[$inc], $nbKey
    dim $_, $ch, $i

    $nbSection    = -1
    do                                                   ; loop through the file contents
        $_ = Trim(ReadLine($fHandle))                    ; remove leading/trailing spaces
        if $_
            $ch = Left($_, 1)
            select
                case $ch="["
                    $ch = Right($_, 1)
                    if $ch="]"                           ; found a section
                        If $nbSection > -1               ; process prior section data, if any
                            gosub _CMIniArray_SavePreviousSection
                        endif

                        $nbKey = -1
                        $nbSection = $nbSection + 1      ; increment the section index
                        if $nbSection > UBound($arrSections)
                            redim Preserve $arrSections[ UBound($arrSections)+$inc ]
                        endif

                        $section = SubStr($_, 2, len($_)-2 )
                    endif

                case $ch=";"                             ; comment line
                case $ch="#"                             ; comment line

                case Len($_) > 2
                    if $nbSection > -1
                        $nbKey = $nbKey + 1
                        if $nbKey > UBound($arrKeys)
                            redim preserve $arrKeys[ UBound($arrKeys)+$inc ]
                        endif

                        $i = instr( $_, "=" )
                        if $i
                            $arrKeys[ $nbKey ] = substr( $_, 1, $i-1), substr( $_, $i+1 )
                        else
                            $arrKeys[ $nbKey ] = $_, ""
                        endif
                    endif

                case 1
            endselect
        endif
    Until @ERROR                                         ; done with input data
    $_ = Close($fHandle)                                 ; close the file

    ;-- process the last/only section --
    If $nbSection > -1
        gosub _CMIniArray_SavePreviousSection

        redim preserve $arrSections[$nbSection]
        $_IniArrayGlobalHandle[$inihandle][2] = $nbsection + 1
        $_IniArrayGlobalHandle[$inihandle][3] = $arrSections
    else
        $_IniArrayGlobalHandle[$inihandle][2] = 0
        $_IniArrayGlobalHandle[$inihandle][3] = ""
    endif

    $CMIniArray = $inihandle                             ; return the array

    Exit 0                                               ; exit success

:_CMIniArray_SavePreviousSection
    if $nbKey=-1
        $arrSections[$nbSection] = $section, 0, ""
    else
        redim preserve $arrKeys[ $nbKey ]
        $arrSections[$nbSection] = $section, ($nbKey+1), $arrKeys
    endif

    return
endfunction


function CMReadIniArray( $inihandle, OPTIONAL $Section, OPTIONAL $Key )
    ;-- in the code --
    ;   $_IniArrayGlobalHandle[$inihandle][2]                     number of sections
    ;   $_IniArrayGlobalHandle[$inihandle][3]                     array of sections
    ;   $_IniArrayGlobalHandle[$inihandle][3][$sectionindex][1]   number of keys in a section
    ;   $_IniArrayGlobalHandle[$inihandle][3][$sectionindex][2]   array of (key, value) in a section
    ;
    ; If the Section is null, return a delimited string of Sections [same as ReadProfileString(file)]
    $CMReadIniArray = ""
    If Not $Section
        if $_IniArrayGlobalHandle[$inihandle][2] > 0
            ;-- enum section name in the inifile --
            for each $section in $_IniArrayGlobalHandle[$inihandle][3]
                $CMReadIniArray = $CMReadIniArray + $section[0] + chr(10)
            next
        endif
        Exit 0
    endif

    ; Search the index for a section
    dim $sectionindex, $i, $item
    $sectionindex = -1
    if $_IniArrayGlobalHandle[$inihandle][2] > 0
        for $i = 0 to UBound($_IniArrayGlobalHandle[$inihandle][3])
            if $_IniArrayGlobalHandle[$inihandle][3][$i][0]=$section
                $sectionindex = $i
            endif
        next
    endif
    If $sectionindex < 0 Exit 2 endif                    ; section not found - Exit

    If Not $Key
        ;-- enum key name in the section --
        if $_IniArrayGlobalHandle[$inihandle][3][$sectionindex][1]<>0
            for each $item in $_IniArrayGlobalHandle[$inihandle][3][$sectionindex][2]
                $CMReadIniArray = $CMReadIniArray + $item[0] + chr(10)
            next
        endif

        Exit 0
    endif

    for each $item in $_IniArrayGlobalHandle[$inihandle][3][$sectionindex][2]
        if $item[0]=$key
            $CMReadIniArray = $item[1]
            exit 0                                       ; key found
        endif
    next

    exit 2                                               ; key not found
endfunction


function CMINISections( $inihandle )
    $CMINISections = CMReadIniArray( $inihandle )
    if Len($CMINISections) > 0
        $CMINISections=split($CMINISections,chr(10))
    endif
endfunction


function CMINIKeys( $inihandle, $section )
    $CMINIKeys = CMReadIniArray( $inihandle, $section )
    if Len($CMINIKeys) > 0
        $CMINIKeys=split($CMINIKeys,chr(10))
    endif
endfunction

;-------------------------------------------------------------------------------
; set of function to read in a ini file (version Glenn)
;-------------------------------------------------------------------------------
;FROM http://www.kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=202790#Post202790
Function IniArray($_fSrcFile, OPTIONAL $_aDataWrite)
    Dim $_                                    ; temp var
    Dim $_Fp                                ; file pointer
    Dim $_I, $_J                            ; index pointers
    Dim $_Sect                                ; Section Name
    Dim $_aDat                                ; Data pair
    Dim $_aSect[1,0], $_aData[1, 0]            ; Section & Data Arrays


    ; 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
                For $_J = 0 to UBound($_aData, 2)        ; key/data pairs
                    If $_aData[1, $_J]                   ; only write keys that have non-null data
                        $_ = $_ + $_aData[0, $_J] + '=' + $_aData[1, $_J] + @CRLF
                    EndIf
                Next
                $_ = WriteLine($_Fp, $_)                 ; write the output line
                If @ERROR Exit @ERROR EndIf              ; exit if error writing
            EndIf
        Next

        $_ = Close($_Fp)                                 ; close the file
        ReDim $_aData[1, 0]                              ; clear array

        ; exit 0 ; do not exit here to force a re-read of the freshly written data
    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 Exit @ERROR EndIf                          ; exit if error opening

    Do                        ; loop through the file contents
        $_ = Trim(ReadLine($_Fp))                        ; remove leading/trailing spaces

        If Left($_, 1) = '['                             ; found a section

            If $_I >= 0                                  ; process prior section data, if any
                $_aSect[1, $_I] = $_aData
                ReDim $_aData[1, 0]
                $_J = -1
            EndIf

            $_I = $_I + 1                                ; increment the section index
            ReDim Preserve $_aSect[1, $_I]
            $_aSect[0, $_I] = Split(SubStr($_, 2), ']')[0]
        Else
            If Not Left($_, 1) = ';' And Not Left($_, 1) = '#' And Len($_) > 2
                $_aDat = Split($_, '=')                  ; break into array
                $_J = $_J + 1                            ; increment the data index
                ReDim Preserve $_aData[1, $_J]
                $_aData[0, $_J] = $_aDat[0]              ; Store the data
                $_aData[1, $_J] = $_aDat[1]              ; Store the data
            EndIf
        EndIf
    Until @ERROR                                         ; done with input data
    $_ = Close($_Fp)                                     ; close the file

    ; process the last/only section
    If $_I >= 0
        $_aSect[1, $_I] = $_aData
    EndIf

    $IniArray = $_aSect                                  ; return the array

    Exit 0                                               ; 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



;Modified FROM http://www.kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=203126#Post203126
function INISections($array)
    $INISections = readiniarray($array)
    if LEN($INISections) > 0
        $INISections=split($INISections,chr(10))
    endif
endfunction

function INIKeys($array,$section)
    $INIKeys = readiniarray($array,$section)
    if LEN($INIKeys) > 0
        $INIKeys=split($INIKeys,chr(10))
    endif
endfunction
.
On a Intel E5200 (Dual-Core 2.5Ghz) with 2Gb, the results are :
 Code:
    size   time1   time2   time3  filename
      91      15       0       0  .\test\file010.ini
    2019       0      16       0  .\test\file020.ini
    7553      16      47      46  .\test\file030.ini
   15173      94    1110    2484  .\test\file040.ini
   28149     187    1704    2296  .\test\file050.ini
   34527      47     250     500  .\test\file060.ini
   43179      94    1031    1938  .\test\file070.ini
   69058     125     515    1125  .\test\file080.ini
  103587     235     797    1922  .\test\file090.ini
  138116     375    1062    2969  .\test\file100.ini

On a Intel Pentium 4 (Mono-Core 2.4Ghz) with 1Gb, the results are :
 Code:
    size   time1   time2   time3  filename
      91       0       0       0  .\test\file010.ini
    2019      15      16      16  .\test\file020.ini
    7553      31      94      93  .\test\file030.ini
   15173     203    2563    6094  .\test\file040.ini
   28149     468    3625    6250  .\test\file050.ini
   34527     125     610    1328  .\test\file060.ini
   43179     218    2532    5015  .\test\file070.ini
   69058     360    1281    3281  .\test\file080.ini
  103587     781    1938    5969  .\test\file090.ini
  138116    1328    2562    9641  .\test\file100.ini

ReadProfileString is always the faster with local file !!!

I use a global array to allocate handle (with associated data). redim is automatic if many differents ini files are used.
I have not tested all cases for CMReadIniArray and I suppress the optional parameter for write in CMIniArray.
Code is not very easy to read because of use of array of array of array ... but it seems to be fast.

Not sure i'll use it in my login script because all procedures are already written to copy inifile locally before using ReadProfileString and delete temp file when finished.
But it was a good exercize for fun ;\)
_________________________
Christophe

Top
#207204 - 2013-04-25 01:13 PM Re: Just Sharing - Adding Printers based on computer name via INI [Re: ChristopheM]
Glenn Barnas Administrator Offline
KiX Supporter
*****

Registered: 2003-01-28
Posts: 4285
Loc: New Jersey
From the testing, there seems to be a threshold where performance drops off dramatically. My login.ini is just under 12K. When I enabled process timestamps, the script completed 600ms faster using IniArray. Most config files are in that 8-12K size, but when I added a few records to the test config, I lost the 600ms and another 2 seconds! I've rolled back to the cached INI file model so performance is consistent.

Thanks for your ideas!

Glenn
_________________________
Actually I am a Rocket Scientist! \:D

Top
Page 2 of 2 <12


Moderator:  Glenn Barnas, NTDOC, Arend_, Jochen, Radimus, Allen, ShaneEP, Mart 
Hop to:
Shout Box

Who's Online
1 registered (Allen) and 191 anonymous users online.
Newest Members
thequeen, ameliaryan, shalomsalami, dkdssdk, Vroon
17639 Registered Users

Generated in 0.038 seconds in which 0.012 seconds were spent on a total of 14 queries. Zlib compression enabled.

Search the board with:
superb Board Search
or try with google:
Google
Web kixtart.org