Announcement

Collapse
No announcement yet.

Sort INI File

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Sort INI File

    I can write a custom function easily enough to sort the entries of each Section in an INI file. But is there a way for Windows to do it for me? Just wondering. I didn't find anything on MSDN. I did find a few internet entries but they just used a custom function approach.

    Another approach is to put the Write statements to the INI file are in the desired sorted order. But if the INI file already exists then you'd have to delete it first . I have a number of apps in which I want the sorted order and would like to avoid adjusting the Write statements in all of them. So adding a function may be the easier way to go.

    #2
    Hey Gary,

    To make sure I understand, the need for sorting is for making life easy when a big ini is viewed in a text editor...

    Comment


      #3
      Gary, not too many of my programs use *.INI files, but those that do, I rewrite the whole *.INI file on exit whether there are changes or not. I load the whole file during initiation and store the information appropriately in memory, and if a component changes between initiating and exiting it will be stored and there for the next run.
      I assume that you'd like to access just specific required features rather than the whole file for either writing or reading.
      Maybe I gotta start writing more complex programs.
      TYPE INI
      ...
      ...
      END TYPE
      comes to mind now that you bring this up.
      Rod
      In some future era, dark matter and dark energy will only be found in Astronomy's Dark Ages.

      Comment


        #4
        Yes, Pierre! That is correct. For when I want to view the file and find out what was save.d

        Rodney, all of my programs use INI files and I rewrite all values to the file each time, regardless of whether they have changed. But the API appears to find the location of the entry and puts it in that location. It does not re-sequence the content of the INI file to match the Write statements. That's a surprising approach,

        Comment


          #5
          You use GetPrivateProfileString/WritePrivateProfileString if I remember well.
          You could write your own ini r/w code: More versatile, more control, more powerful, more fun.
          I can send you a template if interested.

          ... the API appears to find the location of the entry and puts it in that location ...
          I guess you could delete the ini before rewriting it.
          Or do a program that read each ini in all subfolders,
          ask you if it is OK to rewrite it sorted, and then go to the next one, and so on...

          This could be a fun project if you already have code for an editor,
          just add a button and rearrange the whole text on a simple click and save it.
          This way, any ini will be as you like...


          Comment


            #6
            Hey Pierre!

            Yes, deleting before writing would work but then I have make sure the Write statements are in sort order. However, your idea of a project to do all the gb*.ini files in a folder might be a good middleground solution

            I probably won't do anything because the need is not great. Lazy bug that I am! But the idea is there now, just waiting to blossom!

            Comment


              #7
              You use GetPrivateProfileString/WritePrivateProfileString if I remember well.
              Close. You need GetPrivateProfileSection().

              Returns an array (small a) of "key=value" pairs... load to a PB Array (cap A) and call ARRAY SORT. (Just load the pairs.. presumably you want to sort on the keys!)

              Reverse the procedure and use WritePrivateProfileSection to store sorted. I'm not sure that would actually work but I don't see why you'd really need it as long as you can sort it 'as required.'

              . But is there a way for Windows to do it for me?
              Depends on how you define "doing it for you." This is pretty easy if you ask me.


              MCM
              Michael Mattias
              Tal Systems (retired)
              Port Washington WI USA
              [email protected]
              http://www.talsystems.com

              Comment


                #8
                2017? I never did complete the goal ... just lived with it until now.

                In my case I never use multiple sections, just one called "[All]" with everything in it, making a sorted version much easier to accomplish:

                Code:
                Sub SortINIFile(INIFileName As WStringZ * %Max_Path)
                   Local temp$, iCount As Long, tempArray() As String, SectionName$   'section:  [All]
                
                   If IsFalse IsFile(INIFileName) Then Exit Sub  'do nothing unless INI file exists
                
                 ​  Open INIFileName For Binary As #1 : Get$ #1, Lof(1), temp$ : Close #1
                ​​
                   SectionName$ = "[All]" + $CrLf
                ​   Replace SectionName$ With "" In temp$
                   iCount = ParseCount(temp$,$CrLf)
                   ReDim tempArray(iCount)
                   Parse temp$, tempArray(), $CrLf
                
                   Array Sort tempArray(), Collate UCase
                   temp$ = Join$(tempArray(),$CrLf)
                   temp$ = SectionName$ + Trim$(temp$, Any $Spc + $CrLf)
                   Open INIFileName For Output As #1 : Print #1, temp$; : Close #1
                End Sub
                ​

                Comment


                  #9
                  Do you find that gives you a blank first item after the Array Sort with some files??

                  I make it a habit to RTRIM$(temp$,$CRLF) when importing a $CRLF delimited file since the last line of such files frequently ends with a $CRLF and that makes PARSECOUNT one more than the actual number of lines in the file.

                  =========================
                  https://camcopng.com
                  =========================

                  Comment


                    #10
                    Howdy, Stuart!

                    No, I didn't see any blank lines, but your suggestion is appropriate to cover the case where the incoming file does have border characters.

                    Comment


                      #11
                      @ Pierre: You could write your own ini r/w code: More versatile, more control, more powerful, more fun.

                      That does sound like fun! I added to my list of "just do it!".

                      @ Gary: Lazy bug? I think more busy than a beaver in a lumber mill !

                      Comment


                        #12
                        I did something like this long ago, using GetPrivateProfileSectionNames and looped through the result of that to get each section's contents with GetPrivateProfileSection, just like MIchael mentioned. In this case with just one section, even easier. But sorting can result in unwanted result if lines include number, like "File1=" will end up after "File10", etc. If for viewing purpose only, why sort and save? Maybe sort and view would be enough?

                        Comment


                          #13
                          In my case I never use multiple sections, just one called "[All]" with everything in it, making a sorted version much easier to accomplish:

                          Since this thread started and up until I retired.. I just used the Windows API-provided functions (GetPrivateProfileString() et al) and did not cater to manual entry. So I never gave a <insert adjective of choice here> if entries were sorted or not.

                          FWIW, I have to nominate your "[ALL] section" "solution" for Winp-out of the Year. You're Welcome.
                          Michael Mattias
                          Tal Systems (retired)
                          Port Washington WI USA
                          [email protected]
                          http://www.talsystems.com

                          Comment


                            #14
                            You can use “ARRAY SORT darray([index]) [FOR count]” to start after "[All]" to sort a section.
                            You can use “Sorting custom arrays: ARRAY SORT MyType(), CALL MyFunc()” to address anomalies.

                            But if you want to prove your stuff, you’d use memory mapping and sort it in place.
                            Probably take me a month to figure that one out.

                            Comment


                              #15
                              Can try this... (NOT WIDE CHAR READY)

                              Code:
                              $SEP = CHR$(1)
                              $SPH = CHR$(2)
                              
                              '----------------------------------------------------------------------------(')
                              
                              %NumMaxLen = 128
                              
                              '----------------------------------------------------------------------------(')
                              
                              FUNCTION fn_SortINI(BYREF v_strFQFNSrc AS STRING, BYVAL v_boolIgnoreCase AS LONG, BYVAL v_boolBackup AS LONG) AS DWORD
                                 ' +------------------------------------------------------------------------------------------+
                                 '
                                 ' DONT USE ON UNICODE INIs   
                                 ' DONT USE ON UNICODE INIs   
                                 ' DONT USE ON UNICODE INIs   
                                 '
                                 ' +------------------------------------------------------------------------------------------+
                                 ' | v_strFQFNSrc     = Pass the fully qualified path name of INI file                        |
                                 ' | v_boolIgnoreCase = Pass %TRUE or %FALSE if you want to ignore case when sorting          |
                                 ' | v_boolBackup     = Pass %TRUE or %FALSE if you want to create a backup (.isb) of the INI |
                                 ' +------------------------------------------------------------------------------------------+
                                 LOCAL v_astrLine() AS STRING
                                 LOCAL v_dwdArraySize AS DWORD
                                 LOCAL v_dwdIndex AS DWORD
                                 LOCAL v_dwdLineCnt AS DWORD
                                 LOCAL v_hFile AS DWORD
                                 LOCAL v_strDataCurr AS STRING
                                 LOCAL v_strDataNext AS STRING
                                 LOCAL v_strFQFNBak AS STRING
                                 LOCAL v_strFQFNTmp AS STRING
                                 LOCAL v_strKey AS STRING
                                 LOCAL v_strLine AS STRING
                                 LOCAL v_strSection AS STRING
                                 LOCAL v_strWord AS STRING
                                 ' +------------------+
                                 ' | Read the old INI |
                                 ' +------------------+
                                 TRY
                                    OPEN v_strFQFNSrc FOR INPUT ACCESS READ LOCK WRITE AS # v_hFile
                                    DO UNTIL EOF(v_hFile)
                                       LINE INPUT # v_hFile, v_strLine
                                       v_strLine = LTRIM$(v_strLine, ANY $WHITESPACE)
                                       IF LEN(v_strLine) = 0 THEN ITERATE
                                       INCR v_dwdLineCnt
                                       IF v_dwdLineCnt > v_dwdArraySize THEN
                                          v_dwdArraySize += 500
                                          REDIM PRESERVE v_astrLine(1 TO v_dwdArraySize)
                                       END IF
                                       SELECT CASE AS LONG ASC(v_strLine)
                                          CASE 91      ' does it start with "[" (new section)
                                             v_strSection = v_strLine + $SEP
                                             v_astrLine(v_dwdLineCnt) = v_strLine
                                          CASE 59      ' does it start with ";"  (comment)
                                             v_astrLine(v_dwdLineCnt) = v_strSection + v_strLine
                                          CASE ELSE
                                             v_strKey = PARSE$(v_strLine, "=", 1)
                                             ' allow for proper sorting numbered items (works for ###=something or Word###=something)
                                             IF RTRIM$(v_strKey, ANY "0123456789") <> v_strKey THEN
                                                v_strWord = RTRIM$(v_strKey, ANY "0123456789")
                                                v_strKey = v_strWord + RIGHT$(STRING$(%NumMaxLen, $SPH) + MID$(v_strKey, LEN(v_strWord) + 1), %NumMaxLen)
                                                v_strLine = v_strKey + MID$(v_strLine, INSTR(v_strLine, "="))
                                             END IF
                                             v_astrLine(v_dwdLineCnt) = v_strSection + v_strLine
                                       END SELECT
                                    LOOP
                                 CATCH
                                    FUNCTION = ERR
                                    EXIT FUNCTION
                                 END TRY
                                 CLOSE # v_hFile
                                 ' +----------------+
                                 ' | Sort the array |
                                 ' +----------------+
                                 IF v_boolIgnoreCase THEN
                                    ARRAY SORT v_astrLine(1) FOR v_dwdLineCnt, COLLATE UCASE
                                 ELSE
                                    ARRAY SORT v_astrLine(1) FOR v_dwdLineCnt
                                 END IF
                                 ' +--------------------------+
                                 ' | Write the Temp INI (ist) |
                                 ' +--------------------------+
                                 v_strFQFNTmp = PATHNAME$(PATH, v_strFQFNSrc) + GUIDTXT$(GUID$) + ".ist"
                                 v_hFile = FREEFILE
                                 TRY
                                    OPEN v_strFQFNTmp FOR OUTPUT LOCK WRITE AS # v_hFile
                                    FOR v_dwdIndex = 1 TO v_dwdLineCnt
                                       v_strSection = PARSE$(v_astrLine(v_dwdIndex), $SEP, 1)
                                       v_strDataCurr = PARSE$(v_astrLine(v_dwdIndex), $SEP, 2)
                                       IF LEN(v_strDataCurr) THEN
                                          PRINT # v_hFile, REMOVE$(v_strDataCurr, $SPH)
                                          ' Add a spacer line for legibility, but only after the last entry in a group, this assumes comments are at TOP of group, not bottom
                                          IF v_dwdIndex < v_dwdLineCnt THEN
                                             v_strDataNext = PARSE$(v_astrLine(v_dwdIndex + 1), $SEP, 2)
                                             IF ((v_strDataNext = "") OR ((ASC(v_strDataNext) = 59)) AND (ASC(v_strDataCurr) <> 59)) THEN PRINT # v_hFile, ""
                                          END IF
                                       ELSE
                                          PRINT # v_hFile, v_strSection
                                       END IF
                                    NEXT v_dwdIndex
                                 CATCH
                                    FUNCTION = ERR
                                    EXIT FUNCTION
                                 END TRY
                                 CLOSE # v_hFile
                                 ' +-----------------------------------------------------------------------+
                                 ' | rename original to backup (isb), and Temp (ist) to original file name |
                                 ' +-----------------------------------------------------------------------+
                                 v_strFQFNBak = PATHNAME$(PATH, v_strFQFNSrc) + PATHNAME$(NAME, v_strFQFNSrc) + ".isb"
                                 TRY
                                    IF ISFILE(v_strFQFNBak) THEN KILL v_strFQFNBak
                                    NAME v_strFQFNSrc AS v_strFQFNBak
                                    NAME v_strFQFNTmp AS v_strFQFNSrc
                                    IF v_boolBackup = %FALSE THEN KILL v_strFQFNBak
                                 CATCH
                                    FUNCTION = ERR
                                    EXIT FUNCTION
                                 END TRY
                              END FUNCTION
                              Opps, sorry left out the constants... added them in.
                              <b>George W. Bleck</b>
                              <img src='http://www.blecktech.com/myemail.gif'>

                              Comment


                                #16
                                Just be aware that if you have comments within a section they will be sorted to the top of the section no real way around that.
                                <b>George W. Bleck</b>
                                <img src='http://www.blecktech.com/myemail.gif'>

                                Comment


                                  #17
                                  Yeah well, I couldn't resist. Simple IniFile viewer with ability to sort & save posted in Source code forum. Handle with care..
                                  Link: https://forum.powerbasic.com/forum/u...ni-file-viewer

                                  Comment


                                    #18
                                    Borje great looking tool.

                                    I know you had mentioned an issue with sorting keys like ###=something or Key###=something as the ### could be any number of characters, if you do something like I did in your code it can easily be fixed...

                                    Code:
                                    ' v_strLine is "key=something"
                                    ' v_strKey = parse$(v_strLine,"=",1)
                                    ' $SPH=chr$(1) a non printable character that won't appear in a normal INI
                                    ' %NumMaxLen=128 or any width number you expect could exist
                                    IF RTRIM$(v_strKey, ANY "0123456789") <> v_strKey THEN
                                       v_strWord = RTRIM$(v_strKey, ANY "0123456789")
                                       v_strKey = v_strWord + RIGHT$(STRING$(%NumMaxLen, $SPH) + MID$(v_strKey, LEN(v_strWord) + 1), %NumMaxLen)
                                       v_strLine = v_strKey + MID$(v_strLine, INSTR(v_strLine, "="))
                                    END IF​
                                    This will allow proper sorting of those key with numbers... just need to do a REMOVE$(v_strLine, $SPH) before displaying/saving it.
                                    <b>George W. Bleck</b>
                                    <img src='http://www.blecktech.com/myemail.gif'>

                                    Comment


                                      #19
                                      Very nice, George - Thank you! I have updated code in Source code forum so it now uses your code to sort keys ending with numbers correctly. Thanks!

                                      Comment

                                      Working...
                                      X
                                      😀
                                      🥰
                                      🤢
                                      😎
                                      😡
                                      👍
                                      👎