AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Sort INI File By Section (w/ Script) - Ye Olde 2D Array Prob

 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Ask for Help
View previous topic :: View next topic  
Author Message
CannedCheese



Joined: 21 May 2008
Posts: 85

PostPosted: Wed Aug 06, 2008 2:21 am    Post subject: Sort INI File By Section (w/ Script) - Ye Olde 2D Array Prob Reply with quote

The following is a working slow script that will read the following ini file and sort it alphabetically by username...

PlayerDB_Input.ini
Code:

; Save this file as PlayerDB_Input.ini
[Jake]
points=15,599
allowance=$1                   
salary=$12                   
completed=12%
earnings=$18,870                   
date=20080731
[Joe]
points=5,622
allowance=$2                   
salary=$13                   
completed=11%
earnings=$8,479                   
date=20080731
[GMan]
points=40,827
allowance=$4                   
salary=$57                   
completed=10%
earnings=$177,055                   
date=20080731
[Bazooka]
points=7,629
allowance=$2                 
salary=$15                 
completed=15%
earnings=$18,257                 
date=20080731
[d-j]
points=13,580
allowance=$1                 
salary=$23                 
completed=6%
earnings=$14,887                 
date=20080731
[chris]
points=24,284
allowance=$2                   
salary=$15                   
completed=11%
earnings=$45,256                   
date=20080731
[fund]
points=9,996
allowance=$2             
salary=$10               
completed=19%
earnings=$18,988               
date=20080731
[Jake1]
points=15,599
allowance=$1                   
salary=$12                   
completed=12%
earnings=$18,870                   
date=20080731
[Joe1]
points=5,622
allowance=$2                   
salary=$13                   
completed=11%
earnings=$8,479                   
date=20080731
[GMan1]
points=40,827
allowance=$4                   
salary=$57                   
completed=10%
earnings=$177,055                   
date=20080731
[Bazooka1]
points=7,629
allowance=$2                 
salary=$15                 
completed=15%
earnings=$18,257                 
date=20080731
[d-j1]
points=13,580
allowance=$1                 
salary=$23                 
completed=6%
earnings=$14,887                 
date=20080731
[chris1]
points=24,284
allowance=$2                   
salary=$15                   
completed=11%
earnings=$45,256                   
date=20080731
[fund1]
points=9,996
allowance=$2             
salary=$10               
completed=19%
earnings=$18,988               
date=20080731
[Jake1]
points=15,599
allowance=$1                   
salary=$12                   
completed=12%
earnings=$18,870                   
date=20080731
[Joe1]
points=5,622
allowance=$2                   
salary=$13                   
completed=11%
earnings=$8,479                   
date=20080731
[GMan1]
points=40,827
allowance=$4                   
salary=$57                   
completed=10%
earnings=$177,055                   
date=20080731
[Bazooka1]
points=7,629
allowance=$2                 
salary=$15                 
completed=15%
earnings=$18,257                 
date=20080731
[d-j1]
points=13,580
allowance=$1                 
salary=$23                 
completed=6%
earnings=$14,887                 
date=20080731
[chris1]
points=24,284
allowance=$2                   
salary=$15                   
completed=11%
earnings=$45,256                   
date=20080731
[fund1]
points=9,996
allowance=$2             
salary=$10               
completed=19%
earnings=$18,988               
date=20080731
[Jake11]
points=15,599
allowance=$1                   
salary=$12                   
completed=12%
earnings=$18,870                   
date=20080731
[Joe11]
points=5,622
allowance=$2                   
salary=$13                   
completed=11%
earnings=$8,479                   
date=20080731
[GMan11]
points=40,827
allowance=$4                   
salary=$57                   
completed=10%
earnings=$177,055                   
date=20080731
[Bazooka11]
points=7,629
allowance=$2                 
salary=$15                 
completed=15%
earnings=$18,257                 
date=20080731
[d-j11]
points=13,580
allowance=$1                 
salary=$23                 
completed=6%
earnings=$14,887                 
date=20080731
[chris11]
points=24,284
allowance=$2                   
salary=$15                   
completed=11%
earnings=$45,256                   
date=20080731
[fund11]
points=9,996
allowance=$2             
salary=$10               
completed=19%
earnings=$18,988               
date=20080731
[Jake11]
points=15,599
allowance=$1                   
salary=$12                   
completed=12%
earnings=$18,870                   
date=20080731
[Joe11]
points=5,622
allowance=$2                   
salary=$13                   
completed=11%
earnings=$8,479                   
date=20080731
[GMan11]
points=40,827
allowance=$4                   
salary=$57                   
completed=10%
earnings=$177,055                   
date=20080731
[Bazooka11]
points=7,629
allowance=$2                 
salary=$15                 
completed=15%
earnings=$18,257                 
date=20080731
[d-j11]
points=13,580
allowance=$1                 
salary=$23                 
completed=6%
earnings=$14,887                 
date=20080731
[chris11]
points=24,284
allowance=$2                   
salary=$15                   
completed=11%
earnings=$45,256                   
date=20080731
[fund11]
points=9,996
allowance=$2             
salary=$10               
completed=19%
earnings=$18,988               
date=20080731
[Jake111]
points=15,599
allowance=$1                   
salary=$12                   
completed=12%
earnings=$18,870                   
date=20080731
[Joe111]
points=5,622
allowance=$2                   
salary=$13                   
completed=11%
earnings=$8,479                   
date=20080731
[GMan111]
points=40,827
allowance=$4                   
salary=$57                   
completed=10%
earnings=$177,055                   
date=20080731
[Bazooka111]
points=7,629
allowance=$2                 
salary=$15                 
completed=15%
earnings=$18,257                 
date=20080731
[d-j111]
points=13,580
allowance=$1                 
salary=$23                 
completed=6%
earnings=$14,887                 
date=20080731
[chris111]
points=24,284
allowance=$2                   
salary=$15                   
completed=11%
earnings=$45,256                   
date=20080731
[fund111]
points=9,996
allowance=$2             
salary=$10               
completed=19%
earnings=$18,988               
date=20080731


This code will sort it and write the sorted INI File to disk...
Code:

#SingleInstance Force
#Persistent

BeginningTime := A_TickCount / 1000
inireadfile := "PlayerDB_Input.ini" ; file to read from
iniwritefile := "PlayerD_Sorted.ini" ; file to write to (append to)

filedelete % iniwritefile
FileRead, MyFile, % IniReadFile

SetBatchLines -1
Loop, Parse, MyFile, `n
{
  if SubStr(A_LoopField,1,1) = "["
  { 
    NameList := Namelist . SubStr(A_LoopField, 2, StrLen(A_LoopField)-3) . "`n"
  }     
}
Sort, NameList, U
;msgbox % NameList

loop, Parse, NameList, `n
{
  if (StrLen(A_LoopField) < 1)
    break
  iniread, points,% IniReadFile, % A_LoopField,points
  iniwrite, % points, % iniwritefile, % A_LoopField,points
  iniread, allowance,% IniReadFile, % A_LoopField,allowance
  iniwrite, % allowance, % iniwritefile, % A_LoopFIeld,allowance
  iniread, salary,% IniReadFile, % A_LoopField,salary
  iniwrite, % salary, % iniwritefile, % A_LoopField,salary
  iniread, completed,% IniReadFile, % A_LoopField,completed
  iniwrite, % completed, % iniwritefile, % A_LoopField,completed
  iniread, earnings,% IniReadFile, % A_LoopField,earnings
  iniwrite, % earnings, % iniwritefile, % A_LoopField,earnings
  iniread, date,% IniReadFile, % A_LoopField,date,20080731
  iniwrite, % date, % iniwritefile, % A_LoopField,date
  iF !(A_LoopField = "" )
    This_IniList := "[" A_LoopField "]`npoints=" points "`nallowance=" allowance "`nsalary=" salary "`ncompleted" completed "`nearnings=" earnings "`ndate=`" date "`n"
  IniList := IniList . This_iniList
}

EndTime := ((A_TickCount / 1000) - BeginningTime)
run notepad.exe %iniwritefile%
msgbox,,Players_Stats,% EndTime " Seconds"


The code is reeeeeaaaaaallllllyyyy slow when large files are involved. I've written a different routine which builds an index off of the sort routine, and then attempts to parse the variable that the file is loaded into via this index, but this seems even slower.

There's a few posts on sorting 2D arrays, but I'm curious if anyone has any ideas about applying this to an INI file. I'd love to hear some thoughts and I'll be posting my slow index code when I get home again.
Back to top
View user's profile Send private message
engunneer



Joined: 30 Aug 2005
Posts: 6847
Location: Pacific Northwest, US

PostPosted: Wed Aug 06, 2008 3:02 am    Post subject: Reply with quote

sorry to throw a wrench into the works, but why do you need to sort the ini file? that's not normally done. There are a few scripts for importing the entire file quite quickly into your script, then you may access any variable you want.
_________________
Unless otherwise noted, all code is untested.
Common Answers: 1.(Loops, Viruses, etc.) 2. Search 3.RTFM
Back to top
View user's profile Send private message Visit poster's website
[VxE]



Joined: 07 Oct 2006
Posts: 1496

PostPosted: Wed Aug 06, 2008 3:30 am    Post subject: Reply with quote

I presume that sorting the ini is meant to remove duplicates (no, I have no idea why).

Anyways, here's an overhaul that should be much faster at rebuilding the ini file:
Code:
#SingleInstance Force
#Persistent

sections = points/allowance/salary/completed/earnings/date

BeginningTime := A_TickCount / 1000
inireadfile := A_Desktop "\PlayerDB_Input.ini" ; file to read from
iniwritefile := A_Desktop "\PlayerD_Sorted.ini" ; file to write to (append to)

filedelete % iniwritefile
FileRead, MyFile, % IniReadFile

SetBatchLines -1
Loop, Read, %IniReadFile%, %iniwritefile%
{
   Loop, Parse, MyFile, `n, `r
   {
      If StrLen(A_LoopField) < 3 || InStr(A_LoopField, ";")
         continue
      StringSplit, loopvar, A_LoopField, =, %A_Space%`t[]
      if InStr(A_LoopField, "[")
      {
         Loop, parse, A_LoopField, `n, []
            NameList .= (loopname := Vok(loopvar2 := A_Loopfield)) "`n"
         loopvar1 =
      }
      Array2D%loopname%%loopvar1% := loopvar2
   }

   Sort, NameList, U
FileAppend, `; %ErrorLevel% entries were removed as duplicates`n`n
   Loop, PArse, Namelist, `n
   {
      If !A_LoopField
         continue
      name := A_LoopField
      FileAppend, % "[" Array2D%name% "]`n"
      Loop, Parse, Sections, /
         FileAppend, % A_LoopField "=" Array2D%name%%A_LoopField% "`n"
   }
break
}
;msgbox % NameList

EndTime := ((A_TickCount / 1000) - BeginningTime)
; run notepad.exe %iniwritefile%
msgbox,,Players_Stats,% EndTime " Seconds"

Vok(name) ; removes characters from the name that make it
{ ; an invalid variable name. This may need some chars added.
   Loop, parse, name,, ``~!`%^&*(){}-=+`,./<>?`;:'"\|
      newname .= A_LoopField
   return newname
}

_________________
My Home Thread
More Common Answers: [1]. It's in the FAQ [2]. Ternary ( a ? b : c ) guide [3]. Post code inside [code][/code] tags !
Back to top
View user's profile Send private message
CannedCheese



Joined: 21 May 2008
Posts: 85

PostPosted: Thu Aug 07, 2008 2:41 am    Post subject: Reply with quote

engunneer wrote:
sorry to throw a wrench into the works, but why do you need to sort the ini file? that's not normally done. There are a few scripts for importing the entire file quite quickly into your script, then you may access any variable you want.


I realize that you shouldn't normally need to sort an INI file, but I wrote an app that was using INI files as a cheap database and a way to avoid searching for duplicates. Problem is, sometimes I want to edit the INI file manually, and I thought it would be nice to keep things alphabetized. I will probably end up moving to a homegrown csv database with each new line containing the data of a particular person. This got me thinking of faster ways to access and write a basic homemade database. However, I love the fact that the INI file structure is very readable-- I figured that there had to be a way to access any variable by importing a file to a variable, only making one call to load the file, and then writing it back after all fields are done being accessed-- or in this case-- after it has been alphabetized.
Back to top
View user's profile Send private message
CannedCheese



Joined: 21 May 2008
Posts: 85

PostPosted: Thu Aug 07, 2008 8:02 am    Post subject: Reply with quote

@VxE - Your code is so fast it is ridiculous. I had to add A_Space to the end of your Vok function and everything worked great. Sorted a 13k person database in like 3 seconds on my slower work machine. Sick! I think I understand how it works-- I'm wondering if we could run into trouble if we had a name JoeBob as well as a Joe!Bob as the "!" character will be removed, correct? If so, is there a common way to use some kind of method to restore the name to its correct name (I think I use virtually all characters in names, except for maybe tab)? Still, in the 13k I tested it on, I didn't get one duplicate removed (which is good, since there were no duplicates in the source ini file).

Also, doesn't this create (13K names) x (#keys) variables? Before seeing your code, I had an idea to load the file into a variable, parsing it and grabbing every line that began with a "[" (every name), writing an index variable with "Name {tab} FILELINE", sorting it, and then doing a stringsplit on the original variable containing the entire file contents, looking up each value with something like..
Code:

#SingleInstance Force
#Persistent
msgbox Press OK to start!
BeginningTime := A_TickCount / 1000
inireadfile := "PlayerDB.ini" ; file to read from
iniwritefile := "PlayerDB_Sorted.ini" ; file to write to (append to)

FileRead, MyFile, % IniReadFile

SetBatchLines -1
Loop, Parse, MyFile, `n
{
  if SubStr(A_LoopField,1,1) = "["
  { 
    NameIndexList := NameIndexList . SubStr(A_LoopField, 2, StrLen(A_LoopField)-3) . A_Tab . A_Index . "`n"
  }     
}
Sort, NameIndexList, U

stringsplit This_File_Line, MyFile, `n ; create a billion variable array consisting of name {tab} index#
loop, parse, NameIndexList, `n ; e.g. Bob {tab} 12`n Jason {tab} 1, etc.
{   
    StringSplit Index, A_LoopField, %A_Tab% 
    StringSplit Name, This_file_line%Index2%, %A_Tab%
   
    Index := Index2
    Loop {
     
      Key := This_File_Line%Index%
   
      if ((substr(key,1,1) <> "[") || (Index = Index2)) && (strlen(key) > 0)
      { 
        Index := Index2 + A_Index   
        NewFile := NewFile . Key . "`n"
        ;ThisSection .= Key . "`n"
      }
      else Break
    }
    ;Index=
    ;Index2=
    ;ThisSection =
}
filedelete % iniwritefile
fileappend, % NewFile, % iniwritefile

EndTime := ((A_TickCount / 1000) - BeginningTime)
;run notepad.exe %iniwritefile%
msgbox,,Players_Stats,% EndTime " Seconds"


My code takes like 110 seconds, whereas yours takes 3. Dynamically assigning variable names is something that I've only briefly touched on while learning javascript-- I'll definitely have to look at it. I absolutely love how your code gives me access to any key under any name. My current INI file is now around 13k (of 6 or 7 keys each--not totally fixed # of subkeys for each name) and is slightly over 1 MB. Assuming that this file grows to 100x this, is it still feasible to name all of these variables in memory? I'd love to write or use a routine that works much faster than IniRead.

Really appreciate the help. I'm still amazed at how you've been able to incorporate into 1 line what would have taken me 4 in so many cases.
Back to top
View user's profile Send private message
[VxE]



Joined: 07 Oct 2006
Posts: 1496

PostPosted: Thu Aug 07, 2008 8:35 am    Post subject: Reply with quote

CannedCheese wrote:
Also, doesn't this create (13K names) x (#keys) variables?

Yes, it does. But even if each variable holds a thousand characters (1Kb), it would still only take about (13000*8*1000 = 104 Mbs). You could scope those vars inside a function so that they only hog memory for a short time. If you have a typical amount of RAM (2G or more), you'll probably never notice the resource usage. One of the basic tenants of programming is that task = time * memory * processor so by using more memory (in the right way) you can reduce time.

I admit that the Vok() function is a hack (a fugly one too). You could rewrite it to translate illegal characters into legal-but-unique substrings (i.e. "{" into "LEGAL_SUBSTRING_OPEN_BRACKET" or "#LSS_OB#"). Since that function is only used to obtain variable name segments, tweaking it shouldn't effect the data.

Anyways, there are some more tech-wise people on these forums who may contradict the particulars of what I've said, but here's a golden rule: working in memory is always faster than accessing the hard disk. That's the biggest reason why my code is faster.
_________________
My Home Thread
More Common Answers: [1]. It's in the FAQ [2]. Ternary ( a ? b : c ) guide [3]. Post code inside [code][/code] tags !
Back to top
View user's profile Send private message
BoBo²
Guest





PostPosted: Thu Aug 07, 2008 8:58 am    Post subject: Reply with quote

[OT]
Wouldn't it make sense to convert the INI into a csv? There are AHK [csv functions] available to work with those. Output should work with any data base. Even with the poor-mens-db-AKA-Excel. [CSV-Editor] for easy editing. ...
[/OT]
Back to top
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Ask for Help All times are GMT
Page 1 of 1

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group