Inifiles with markup support
Inifiles are boring, stiff and bordering on jurrasic park technology-wise; Yet most Delphi developers end up with TInifile for both preferences and language medium. Inifiles may be simple, but they are effective and to the point.
But can we improve on the formula? Let’s jump into the nearest Tardis and fly back around 13 years, thats when I wrote this class (and I still use it to this day).
Markup?
The code below let’s you define key/value pairs as normal, but also re-use them as parts of other key/value pairs. This makes it so much easier to write language-files, especially long strings that are 90% identical.
How do i use it?
First, lets look at a small example. Its so simple that it requires only seconds to get it, yet i find it useful in it’s simplicity:
[database] server1=192.0.2.62 server2=192.0.2.63 server3=192.0.2.64 server4=192.0.2.65 port=143 file="payroll.dat"
Notice how the IP addresse are 90% identical? Well, with my little class you can now do this:
[database] root=192.0.2. server1={database:root}.62 server2={database:root}.63 server3={database:root}.64 server4={database:root}.65 port=143 file="payroll.dat"
Now you dont save much space, but you gain the ability to define and re-use values within the same ini-file. And language files can grow large quickly, so the more you can re-use segments and phrases, the better the language file will be.
[DISK] internal_error=An internal error occured, file_error=A file error occured, file_write_error = {DISK:file_error} write operation failed (%s) file_read_error = {DISK:file_error} read operation failed (%s) file_lock_error = {DISK:file_error} unable to obtain file-lock (%s)
I also added support for CRLF, for this we use the “|” (pipe) symbol. Its also thread safe, which is a bonus.
Enjoy!
unit jllanguage; interface uses sysutils, classes, inifiles, syncobjs; Const CNT_JL_LANGUAGEFILE_MACRO_BEGIN = '{'; CNT_JL_LANGUAGEFILE_MACRO_END = '}'; CNT_JL_LANGUAGEFILE_LINEFEED = '|'; CNT_JL_LANGUAGEFILE_NAMESTOP = ':'; type TJLLanguageFile = Class(TComponent) Private FLock: TCriticalSection; FLanguage: TInifile; FFilename: TFileName; Protected Function GetActive:Boolean; Procedure SetActive(Value:Boolean); Procedure SetFileName(Value:TFileName); Function IniFormat(IniFile:TCustomIniFile; Value:String):String; Public Function ReadStr(Section,Ident,DefValue:String):String; Function ReadInt(Section,Ident:String;DefValue:Integer):Integer; Function ReadCurr(Section,Ident:String;DefValue:Currency):Currency; Function FormatLangStr(Value:String):String; Function Open(Filename:String):Boolean; Function Close:Boolean; Published Property Filename:TFilename read FFilename write SetFileName; Property Enabled:Boolean read GetActive write SetActive; Constructor Create(AOwner:TComponent);override; Destructor Destroy;Override; End; implementation //########################################################################### // TJLLanguageFile //########################################################################### Constructor TJLLanguageFile.Create(AOwner:TComponent); Begin inherited Create(Aowner); FLock:=TCriticalSection.Create; FLanguage:=NIL; end; Destructor TJLLanguageFile.Destroy; Begin FLock.Enter; try if FLanguage<>NIl then FreeAndNIL(FLanguage); finally FLock.Leave; end; FreeAndNIL(FLock); inherited; end; Function TJLLanguageFile.GetActive:Boolean; Begin FLock.Enter; try result:=assigned(FLanguage); finally FLock.Leave; end; end; Procedure TJLLanguageFile.SetActive(Value:Boolean); Begin If Value<>GetActive then Begin Close; if Value then Open(FFilename); end; end; Procedure TJLLanguageFile.SetFileName(Value:TFilename); Begin if (csDesigning in ComponentState) then FFilename:=Value else Begin If not GetActive then FFileName:=Value else Raise Exception.Create ('Filename can not be changed in active component'); end; end; Function TJLLanguageFile.IniFormat(IniFile:TCustomIniFile; Value:String):String; var x,y: Integer; FLen: Integer; FEnd: Integer; FName: String; FSec: String; Begin Value:=trim(Value); Flen:=Length(Value); SetLength(result,0); If FLen>0 then Begin x:=0; while x<FLen do Begin inc(x); if Value[x]=CNT_JL_LANGUAGEFILE_MACRO_BEGIN then Begin (* locate end of macro *) FEnd:=0; for y:=x+1 to FLen do Begin if value[y]=CNT_JL_LANGUAGEFILE_MACRO_END then Begin FEnd:=y; Break; end; end; if FEnd>0 then Begin FName:=Copy(Value,x+1,(y-x)-1); x:=y; y:=pos(CNT_JL_LANGUAGEFILE_NAMESTOP,FName); if y>0 then Begin FSec:=Copy(FName,1,y-1); Delete(FName,1,y); result:=result + IniFile.ReadString(FSec,FName,''); end else result:=result + CNT_JL_LANGUAGEFILE_MACRO_BEGIN + FName + CNT_JL_LANGUAGEFILE_MACRO_END; end else result:=result + Value[x]; end else result:=result + Value[x]; end; end; end; Function TJLLanguageFile.Open(Filename:String):Boolean; Begin result:=FileExists(Filename); If result then Begin FLock.Enter; try result:=False; (* release previous language data *) if assigned(Flanguage) then FreeAndNil(Flanguage); try Flanguage:=TIniFile.Create(Filename); result:=True; except on exception do exit; end; finally FLock.Leave; end; end; end; Function TJLLanguageFile.Close:Boolean; Begin FLock.Enter; try result:=assigned(Flanguage); If result then FreeAndNIL(Flanguage); finally FLock.Leave; end; end; Function TJLLanguageFile.FormatLangStr(Value:String):String; Begin FLock.Enter; try If Flanguage<>NIL then Begin if pos(CNT_JL_LANGUAGEFILE_MACRO_BEGIN,result)>0 then result:=IniFormat(Flanguage,result) else result:=Value; end else result:=Value; finally FLock.Leave; end; end; Function TJLLanguageFile.ReadStr(Section,Ident,DefValue:String):String; Begin FLock.Enter; try If Flanguage<>NIL then Begin result:=Flanguage.ReadString(Section,Ident,DefValue); (* add support for extended formating *) if pos(CNT_JL_LANGUAGEFILE_MACRO_BEGIN,result)>0 then result:=IniFormat(Flanguage,result); if pos(CNT_JL_LANGUAGEFILE_LINEFEED,result)>0 then result:=StringReplace(result,CNT_JL_LANGUAGEFILE_LINEFEED, #13,[rfReplaceAll]); end else result:=DefValue; finally FLock.Leave; end; end; Function TJLLanguageFile.ReadInt(Section,Ident:String; DefValue:Integer):Integer; Begin FLock.Enter; try If Flanguage<>NIL then result:=Flanguage.ReadInteger(Section,Ident,DefValue) else result:=DefValue; finally FLock.Leave; end; end; Function TJLLanguageFile.ReadCurr(Section,Ident:String; DefValue:Currency):Currency; Begin FLock.Enter; try If Flanguage<>NIL then result:=FLanguage.ReadFloat(section,ident,defvalue) else result:=DefValue; finally FLock.Leave; end; end; end.
Leave a Reply Cancel reply
Recent
The vatican vault
- January 2022
- October 2021
- March 2021
- November 2020
- September 2020
- July 2020
- June 2020
- April 2020
- March 2020
- February 2020
- January 2020
- November 2019
- October 2019
- September 2019
- August 2019
- July 2019
- June 2019
- May 2019
- April 2019
- March 2019
- February 2019
- January 2019
- December 2018
- November 2018
- October 2018
- September 2018
- August 2018
- July 2018
- June 2018
- May 2018
- April 2018
- March 2018
- February 2018
- January 2018
- December 2017
- November 2017
- October 2017
- August 2017
- July 2017
- June 2017
- May 2017
- April 2017
- March 2017
- February 2017
- January 2017
- December 2016
- November 2016
- October 2016
- September 2016
- August 2016
- July 2016
- June 2016
- May 2016
- April 2016
- March 2016
- January 2016
- December 2015
- November 2015
- October 2015
- September 2015
- August 2015
- June 2015
- May 2015
- April 2015
- March 2015
- February 2015
- January 2015
- December 2014
- November 2014
- October 2014
- September 2014
- August 2014
- July 2014
- June 2014
- May 2014
- April 2014
- March 2014
- February 2014
- January 2014
- December 2013
- November 2013
- October 2013
- September 2013
- August 2013
- July 2013
- June 2013
- May 2013
- February 2013
- August 2012
- June 2012
- May 2012
- April 2012
This looks very similar to something I created (and heavily use) several years ago. See http://zarko-gajic.iz.hr/tliveini-custom-delphi-ini-implementation/
Great minds think alike 🙂