AutoHotkey Community

It is currently May 26th, 2012, 1:14 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 33 posts ]  Go to page Previous  1, 2, 3  Next
Author Message
 Post subject:
PostPosted: August 31st, 2007, 2:34 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
I added link to your function to the first post.

Thx for your 3 minutes, and I am glad to hear that you have 2 minutes left for something else.

Quote:
I'll wait on you for the reverse functions lol.

Ofcourse. I will do slow version and send you for optimisation if you can spare another minute.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 21st, 2007, 11:25 am 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
2Titan
I finaly had time to check out your version, and it was like i was telling you, not good.

First the point is to make function universal, that is to respond on all cases of ini files:

For instance
Code:
 If InStr(A_LoopReadLine, "[") = 1


is one of such mistakes cuz I can have ini files like this:

Code:
[section1]
hej1= value

   [section2]
      hej    =  value

in which case section2 will not be spoted, while IniRead does this nicely.


When you account all that iregular variants of regular ini files, your code will get somewhat bigger and thus will not work that faster as RE as it is entirely executed in C code

Well, you can try in another 3 minuts to solve mentioned problems. Its not a big deal after all, but we got to 6 now ;)

In other way, the point of your function was not the same as point of the mine so it can't replace 2 functions I gave. Its not point to parse everything into global vars, but to have Ini framework that works with strings in INI form as much as with INI files.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 21st, 2007, 3:51 pm 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7501
Location: Australia
I recently started to write ini file reading/parsing code for one of the scripts I'm working on, but happily deleted it all when I found GetPrivateProfileSection and related functions.

Here are the wrappers I'm using, in case they're of some use to someone:
Code:
; Reads an entire section of an ini file, excluding comments.
INI_ReadSection(Filename, Section)
{
    ; Expand relative paths, since GetPrivateProfileSection only searches %A_WinDir%.
    Loop, %Filename%, 0
        Filename := A_LoopFileLongPath
   
    VarSetCapacity(text, 0x7FFF, 0)

    len := DllCall("GetPrivateProfileSection"
        , "str", Section, "str", text, "uint", 0x7FFF, "str", Filename)
   
    ; Each line within the section is terminated with a null character.
    ; Replace each delimiting null char with a newline:
    Loop, % len-1
        if (NumGet(text, A_Index-1, "UChar") = 0)
            NumPut(10, text, A_Index-1, "UChar")  ; \0 -> \n

    ; Windows Me/98/95:
    ;   The returned string includes comments.
    ;
    ; This removes comments. Also, I'm not sure if leading/trailing space is
    ; automatically removed on Win9x, so the regex removes that too.
    if A_OSVersion in WIN_ME,WIN_98,WIN_95
        text := RegExReplace(text, "m`n)^[ `t]*(?:;.*`n?|`n)|^[ `t]+|[ `t]+$")
   
    return text
}

; Replaces the content of a section,
; from [section] to the last key=value line.
INI_WriteSection(Filename, Section, Content)
{
    Loop, %Filename%, 0
        Filename := A_LoopFileLongPath
   
    if (SubStr(Content,0) != "`n")
        Content .= "`n"  ; `n -> section null-terminator
   
    Loop, % StrLen(Content)
        if (NumGet(Content, A_Index-1, "UChar") = 10)
            NumPut(0, Content, A_Index-1, "UChar")  ; \n -> \0
   
    ; Note: This deletes ALL existing content between [Section] and the last
    ;       key=value line, but not trailing lines which have no '='.
    return DllCall("WritePrivateProfileSection"
        , "str", Section, "str", Content, "str", Filename)
}

; Gets the names of all sections in an ini file.
INI_GetSectionNames(Filename)
{
    ; Expand relative paths, since GetPrivateProfileSectionNames only searches %A_WinDir%.
    Loop, %Filename%, 0
        Filename := A_LoopFileLongPath
   
    VarSetCapacity(text, 0x1000, 0)
   
    len := DllCall("GetPrivateProfileSectionNames", "str", text, "uint", 0x1000, "str", Filename)
   
    ; Replace each delimiting null char with a newline:
    Loop, % len-1
        if (NumGet(text, A_Index-1, "UChar") = 0)
            NumPut(10, text, A_Index-1, "UChar")  ; \0 -> \n
   
    return text
}
Very little parsing is necessary, as the Win32 API does most of the work.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 21st, 2007, 4:29 pm 
Offline
User avatar

Joined: August 11th, 2004, 1:47 am
Posts: 5347
Location: UK
majkinetor wrote:
Code:
   [section2]
I had AutoTrim in my code anyway so just replace the relevant lines of code with:
Code:
   Loop, Read, %src%
   {
      l = %A_LoopReadLine%
      If InStr(l, "[") = 1
      {
         StringMid, s, l, 2, InStr(l, "]") - 2


My point was not only about speed but the fact that complex regex which even veterans like Skan and engunneer struggle with can be avoided with simple string functions or Win API as lexikos nicely demonstrated above (ever head the expression: you don't need a bulldozer to build a sandcastle??).

_________________
GitHubScriptsIronAHK Contact by email not private message.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 21st, 2007, 9:17 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Quote:
My point was not only about speed but the fact that complex regex which even veterans like Skan and engunneer struggle with

It seems all of you didn't do your homework, rofl

Quote:
I recently started to write ini file reading/parsing code for one of the scripts I'm working on, but happily deleted it all when I found GetPrivateProfileSection and related functions.

I am aware of all system ini functions and I intentionaly choosed to avoid them. Can't remember right now what was the reason... perhaps I have currently to much alchohol in my blood 8)

Quote:
(ever head the expression: you don't need a bulldozer to build a sandcastle??)

I prefer to use bulldozer if it is already at the spot and ready for work. rofl 8)

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 18th, 2007, 4:28 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
New version
Documentation is online now.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 18th, 2007, 9:55 pm 
Offline

Joined: July 16th, 2005, 11:39 am
Posts: 96
majkinetor wrote:
I am more concerned currently about API that is extremely practical and easy to use in most cases.


I'm not dissing anyone else's viewpoints here, but thanks for writing a set of functions that I find both useful and fairly easy to use.
Regardless of how well-coded or well-optimized a function is, it's useless to me if I can't figure out how to get it to work.
I also like the flexibility,and I learned a little syntax candy from reading your code, too.
Great documentation, btw.

_________________
“yields falsehood when preceded by its quotation” yields falsehood when preceded by its quotation.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 19th, 2007, 12:06 am 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Thank you :)

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 19th, 2007, 5:07 am 
Offline

Joined: November 24th, 2007, 9:07 pm
Posts: 774
I really like these functions too. I'm going to start using them!

I use AHKArray in my script, but for some things that just seems slightly like overkill. In those cases, this should do the trick nicely to avoid having to write a bunch of different globals!

I could probably even load all my program settings with this and parse them on the fly rather than have a global per-setting. That would be interesting!

Thanks for the cool functions majkinetor... (as usual :) )

_________________
Ben

My Trac projects
My Wiki
[Broken] - My music


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 19th, 2007, 9:49 am 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Quote:
I could probably even load all my program settings with this and parse them on the fly rather than have a global per-setting.

Thats one of the points of the module. You can just call Ini_LoadSections("config.ini") and start to use settings imediately in the manner %Section_key%. So, instead calling IniRead multiple times, you do only one func call, which is also faster then multiple InIReads.

Keep in mind that you can use functions on any regular string. You can use it to return multiple values from the function, for instance:

Code:
fun(...) {
 ;here we have values in val1, val2, val3... valN
 ;now we make the section format
 loop, N
     res .= "ret" %A_Index% "=" val%A_Index% "`n"
 
 return res
}

Ini_GetVal( fun(), "ret5") ;get ret value 5
Ini_LoadKeys("", fun(), "", "fun_")  ;makes fun_ret1, fun_ret2 ... fun_retN

vals := Ini_LoadKeys("", fun(), "vals")  ;get only values function returned....
loop, parse, %vals%, `n
    ;do something with values here


And so on...

Quote:
Thanks for the cool functions majkinetor

Thanks :)

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 19th, 2007, 4:00 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
v1.0 b2

Big update, added MakeSection function which is reverse function of LoadKeys, so this should produce the same section string as input (up to the trimming of key name):

Code:
s =
(
key1=blah blah
mykey= what now ?
thirdOne= >.<
)

Ini_LoadKeys("", s, "", "section")
s1 := Ini_MakeSection("section_")

msgbox  % s1


Keep in mind that this function is xperimental anyway, and in some weird cases it may fail. See this

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 27th, 2007, 7:27 pm 
Offline

Joined: November 24th, 2007, 9:07 pm
Posts: 774
Either I'm doing something wrong or I'm just getting some unexpected results here.

Here is the code that loads from my config file:
Code:
ReadConfig:
   FileCopy,res\default\config.ini.default,%ConfigFile%,0
   configSections := Ini_GetSectionNames(ConfigFile)
   Loop, Parse, configSections, `n
      If (A_LoopField <> "Disabled")
         Ini_LoadKeys(ConfigFile, A_LoopField, 0, "cfg_" . A_LoopField . "_")
   If Not cfg_SteamLab_LastRun
      GoSub,ShowSettings
Return

And here is the code that saves back to it:
Code:
WriteConfig:
   Loop, Parse, configSections, `n
      If (A_LoopField <> "Disabled") {
         section := Ini_MakeSection("cfg_" . A_LoopField . "_")
         Ini_ReplaceSection(ConfigFile, A_LoopField, section)
      }
Return


Here is the original config file:
Code:
[SteamLab]
LastRun=
LoadFont=Share-TechMono.ttf
Icon=res\ico\SteamLab.ico

[Schedule]
Enable=0
CheckDuration=3
LastCheck=

[Backup]
GameDir=backup\games
FileDir=backup\files

[Steam]
Version=
Username=
Password=
RunOnStart=1
LockPassword=
Locked=0
DownloadURL=http://storefront.steampowered.com/download/SteamInstall.msi
RegKey=HKEY_CURRENT_USER\Software\Valve\Steam\SteamPath

[Crosus]
Enable=0
Version=
Username=
Password=
DownloadURL=http://www.isotx.com/CrosuSInstaller.exe

[OBMM]
Enable=0
Version=
DownloadURL=http://timeslip.chorrol.com/current/obmm.zip
RegKey=HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Oblivion mod manager_is1\InstallLocation

[SteamDock]
Enable=1
Enabled=0

[OSD]
Color=Blue
Font=Share-TechMono

[Hotkeys]
Enable=1

[Screensaver]
Disabled=1

[SteamWin]
BackupManW=450
BackupManH=292
BackupManX=
BackupManY=
BackupManGui=14
KeyManW=
KeyManH=
KeyManX=
KeyManY=
HotkeyManW=
HotkeyManH=
HotkeyManX=
HotkeyManY=
SettingsW=
SettingsH=
SettingsX=
SettingsY=

Here is the config file after being loaded, some values being updated, and it being saved with the above function:
Code:
[SteamLab]
Icon=
LastRun=20071227121912
LoadFont=
SteamLab_Icon=
SteamLab_LastRun=20071227114235
[Schedule]
CheckDuration=
Enable=
Schedule_CheckDuration=
Schedule_Enable=
Schedule_LastCheck=
[Backup]
[Steam]
RunOnStart=
Steam_DownloadURL=
Steam_Locked=
Steam_LockPassword=
Steam_Path=0
Steam_PID=
Steam_RegKey=
Steam_RunOnStart=
[Crosus]
Crosus_DownloadURL=
Crosus_Enable=
Crosus_Path=
Crosus_Path_bG=SetCrosusPath
Crosus_Path_bH=28
Crosus_Path_bO=x+0 yp-6
Crosus_Path_bW=76
Crosus_Path_hwnd=0xb0386
Crosus_Pathb1=1443172427
Crosus_Pathb1_ro=1225068657
Crosus_PID=
Crosus_Version=
Enable=
[OBMM]
Enable=
OBMM_Enable=
OBMM_Path=
OBMM_PID=
OBMM_RegKey=
[Dock]
Dock_aClient[0]=
Dock_Enable=
Dock_Enabled=
Dock_hHook1=
Dock_hHook2=
Dock_hHook3=
Dock_hookProcAdr=
Dock_HostDied=
Dock_HostID=
Dock_OnHostDeath=
Dock_s1=
[OSD]
OSD_Color=
[Hotkeys]
Enable=
Hotkeys_Enable=
Hotkeys_Section=


I don't understand several things.

What happened to all my [Backup] values?
Why did my Dock_ variables get put in a section even though there was no Dock section to begin with?
Should there not be two underscores in the prefix? It seems to be writing the name as everything after the first underscore.

I don't have any variables such as cfg_Crosus_Path_bG
or cfg_Crosus_Path_bH
so it must be getting those values from the variables Crosus_Path_bG and Crosus_Path_bH

Update: Even if I remove the first underscore from the prefix, it still removes all the keys under [Backup], removes the empty line in-between sections, is not saving my loaded variables back to the file, only ones I've set afterward.

_________________
Ben

My Trac projects
My Wiki
[Broken] - My music


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 27th, 2007, 9:54 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
It seems this is a bug in MakeSection code. I will check it out as soon as I can.

About other usage, u can achieve the same as above significantly faster with just 1 command:

Code:
ReadConfig:
   FileCopy,res\default\config.ini.default,%ConfigFile%,0
   configSections := Ini_GetSectionNames(ConfigFile)
   Loop, Parse, configSections, `n
      If (A_LoopField <> "Disabled")
         Ini_LoadKeys(ConfigFile, A_LoopField, 0, "cfg_" . A_LoopField . "_")
   If Not cfg_SteamLab_LastRun
      GoSub,ShowSettings
Return


Code:
 Ini_LoadKeys(ConfigFile, "", 0, "cfg_")

The bad thing here is that Disabled will also be loaded as there is no filter for sections in this mode. However, you may use filter if all keys in the Disabled section have common prefix. Like this, ini file is read only once, wile in your code it will be read as the number of sections in it. You can also use second mechanism, to first call Ini_LoadSection(ConfigFile, "") to load all sections in global section strings, and then feed strings to Ini_LoadKeys and blank for file name. That will also be much faster and ini is again read only once. This way you can also delete Disabled section with inis_Disabled := ""

Keep in mind that MakeSection is experimental function, and it will fail if you have large number of variables due to the limitations of AHK. This is not the case here however.

I currently discourage its usage but its ofcourse xtremely handy - your entire configuration work can be done with it in few lines as you demonstrated, reglardles of number of configuration options. Actually, with the code you wrote you don't have to think about Ini ever again (when I fix MakeSection ofc). Contrary to that, standard AHK ini handling would need much much more lines of code and constant maintance when adding/ removing options.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 28th, 2007, 12:47 am 
Offline

Joined: November 24th, 2007, 9:07 pm
Posts: 774
Heh, imagine my surprise when all my Dock() globals showed up in my config file :)

That's why the functions are so attractive :) I used to use tons of Ini, Writes and Ini, Reads to save and load, but every time I made a change to the config file I had to update the code. Once the function works right I'll hopefully be able to create whatever options I want and just conform my code to the Ini prefixes so that I work directly with the config variables. I've already made most of the changes in my code, but SteamLab isn't currently working properly due to those new saving and loading functions (or I guess probably just the MakeSection code) :wink:

Now that I think about it, I might as well just pull the Disabled variables in with the Ini code anyway, so that ultimately those variables can be accessed (and therefore re-enabled) if needed by the script later on. It shouldn't hurt anything I suppose. So I'll shorten my loading function up. Thanks for the tip :)

Now if the saving function worked properly I think I'd be in business!
Code:
ReadConfig:
   FileCopy,res\default\config.ini.default,%ConfigFile%,0
   configSections := Ini_GetSectionNames(ConfigFile)
   Ini_LoadKeys(ConfigFile, "", 0, "cfg_")
   If Not cfgSteamLab_LastRun
      GoSub,ShowSettings
Return
WriteConfig:
   Loop, Parse, configSections, `n
   {   section := Ini_MakeSection("cfg_" . A_LoopField . "_")
      Ini_ReplaceSection(ConfigFile, A_LoopField, section)
   }
Return

_________________
Ben

My Trac projects
My Wiki
[Broken] - My music


Last edited by bmcclure on December 28th, 2007, 1:29 am, edited 1 time in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: December 28th, 2007, 1:23 am 
Offline

Joined: November 24th, 2007, 9:07 pm
Posts: 774
(when makesection works) would this be faster than doing a ReplaceSection for each section?
Code:
WriteConfig:
   configFileNew := ""
   Loop, Parse, configSections, `n
      configFileNew .= "[" . A_LoopField . "]`n" . Ini_MakeSection("cfg_" . A_LoopField . "_") . "`n`n"
   FileDelete, %ConfigFile%
   FileAppend, %configFileNew%, %ConfigFile%
Return

I figure that way it only makes two file operations instead of one per section.

Update: Hey, good news. I tried my script with the above code, and aside from the bogus keys that MakeSection inserts, the Ini File is looking great now:
Code:
[SteamLab]
Icon=
LastRun=20071227181234
LoadFont=
SteamLab_LastRun=20071227124654
SteamLab_LoadFont=
SteamLab_SteamLab_Icon=res\ico\SteamLab.ico
SteamLab_SteamLab_LastRun=
SteamLab_SteamLab_LoadFont=Share-TechMono.ttf

[Schedule]
CheckDuration=
Enable=
Schedule_CheckDuration=
Schedule_Enable=
Schedule_Schedule_CheckDuration=3
Schedule_Schedule_Enable=0
Schedule_Schedule_LastCheck=

[Backup]
Auto=
Backup_Auto=
Backup_Backup_Auto=1
Backup_Backup_FileDir=backup\files
Backup_Backup_GameDir=backup\games

[Steam]
AutoRun=
Steam_AutoRun=
Steam_Steam_AutoRun=1
Steam_Steam_DownloadURL=http://storefront.steampowered.com/download/SteamInstall.msi
Steam_Steam_Locked=0
Steam_Steam_LockPassword=
Steam_Steam_Password=
Steam_Steam_RegKey=HKEY_CURRENT_USER\Software\Valve\Steam\SteamPath
Steam_Steam_Username=
Steam_Steam_Version=

[Crosus]
Crosus_Crosus_DownloadURL=http://www.isotx.com/CrosuSInstaller.exe
Crosus_Crosus_Enable=0
Crosus_Crosus_Password=
Crosus_Crosus_Username=
Crosus_Crosus_Version=
Crosus_Enable=
Enable=

[OBMM]
Enable=
OBMM_Enable=
OBMM_OBMM_DownloadURL=http://timeslip.chorrol.com/current/obmm.zip
OBMM_OBMM_Enable=0
OBMM_OBMM_RegKey=HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Oblivion mod manager_is1\InstallLocation
OBMM_OBMM_Version=

[SteamDock]
Enable=
SteamDock_Enable=
SteamDock_SteamDock_Enable=1
SteamDock_SteamDock_Enabled=0

[OSD]
OSD_OSD_Color=Blue
OSD_OSD_Font=Share-TechMono

[Hotkeys]
Enable=
Hotkeys_Enable=
Hotkeys_Hotkeys_Enable=1

[Screensaver]
Screensaver_Screensaver_Disabled=1

[SteamWin]
SettingsH=228
SettingsW=369
SettingsX=655
SettingsY=396
SteamWin_SettingsH=228
SteamWin_SettingsW=369
SteamWin_SettingsX=655
SteamWin_SettingsY=396
SteamWin_SteamWin_BackupManGui=14
SteamWin_SteamWin_BackupManH=292
SteamWin_SteamWin_BackupManW=450
SteamWin_SteamWin_BackupManX=
SteamWin_SteamWin_BackupManY=
SteamWin_SteamWin_HotkeyManH=
SteamWin_SteamWin_HotkeyManW=
SteamWin_SteamWin_HotkeyManX=
SteamWin_SteamWin_HotkeyManY=
SteamWin_SteamWin_KeyManH=
SteamWin_SteamWin_KeyManW=
SteamWin_SteamWin_KeyManX=
SteamWin_SteamWin_KeyManY=
SteamWin_SteamWin_SettingsH=
SteamWin_SteamWin_SettingsW=
SteamWin_SteamWin_SettingsX=
SteamWin_SteamWin_SettingsY=

At least my dock() globals are nowhere to be found in there now, and my [Backup] keys show up now :)

_________________
Ben

My Trac projects
My Wiki
[Broken] - My music


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 33 posts ]  Go to page Previous  1, 2, 3  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: Google Feedfetcher and 55 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group