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 

[module] Ini 1.0b2 - ini-like handling of strings
Goto page Previous  1, 2, 3  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
majkinetor



Joined: 24 May 2006
Posts: 4116
Location: Belgrade

PostPosted: Fri Aug 31, 2007 2:34 pm    Post subject: Reply with quote

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.
_________________
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 4116
Location: Belgrade

PostPosted: Fri Sep 21, 2007 11:25 am    Post subject: Reply with quote

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 Wink

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.
_________________
Back to top
View user's profile Send private message
Lexikos



Joined: 17 Oct 2006
Posts: 4473
Location: Qld, Australia

PostPosted: Fri Sep 21, 2007 3:51 pm    Post subject: Reply with 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.

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.
Back to top
View user's profile Send private message Visit poster's website
Titan



Joined: 11 Aug 2004
Posts: 5043
Location: /b/

PostPosted: Fri Sep 21, 2007 4:29 pm    Post subject: Reply with quote

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??).
_________________
Chat (IRC)PlusNetScriptsIronAHK Contact by email not private message.
Back to top
View user's profile Send private message Send e-mail Visit poster's website
majkinetor



Joined: 24 May 2006
Posts: 4116
Location: Belgrade

PostPosted: Fri Sep 21, 2007 9:17 pm    Post subject: Reply with quote

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 Cool

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 Cool
_________________
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 4116
Location: Belgrade

PostPosted: Tue Dec 18, 2007 4:28 pm    Post subject: Reply with quote

New version
Documentation is online now.
_________________
Back to top
View user's profile Send private message
Dragonscloud



Joined: 16 Jul 2005
Posts: 95

PostPosted: Tue Dec 18, 2007 9:55 pm    Post subject: Reply with quote

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.
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 4116
Location: Belgrade

PostPosted: Wed Dec 19, 2007 12:06 am    Post subject: Reply with quote

Thank you Smile
_________________
Back to top
View user's profile Send private message
bmcclure



Joined: 24 Nov 2007
Posts: 766

PostPosted: Wed Dec 19, 2007 5:07 am    Post subject: Reply with quote

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 Smile )
_________________
Ben

My Trac projects
My Wiki
[Broken] - My music
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 4116
Location: Belgrade

PostPosted: Wed Dec 19, 2007 9:49 am    Post subject: Reply with quote

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 Smile
_________________
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 4116
Location: Belgrade

PostPosted: Wed Dec 19, 2007 4:00 pm    Post subject: Reply with quote

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
_________________
Back to top
View user's profile Send private message
bmcclure



Joined: 24 Nov 2007
Posts: 766

PostPosted: Thu Dec 27, 2007 7:27 pm    Post subject: Reply with quote

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
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 4116
Location: Belgrade

PostPosted: Thu Dec 27, 2007 9:54 pm    Post subject: Reply with quote

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.
_________________
Back to top
View user's profile Send private message
bmcclure



Joined: 24 Nov 2007
Posts: 766

PostPosted: Fri Dec 28, 2007 12:47 am    Post subject: Reply with quote

Heh, imagine my surprise when all my Dock() globals showed up in my config file Smile

That's why the functions are so attractive Smile 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 Smile

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 Fri Dec 28, 2007 1:29 am; edited 1 time in total
Back to top
View user's profile Send private message
bmcclure



Joined: 24 Nov 2007
Posts: 766

PostPosted: Fri Dec 28, 2007 1:23 am    Post subject: Reply with quote

(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 Smile
_________________
Ben

My Trac projects
My Wiki
[Broken] - My music
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page Previous  1, 2, 3  Next
Page 2 of 3

 
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