INTELLIGENT WORK FORUMS FOR COMPUTER PROFESSIONALS
Come Join Us!
Are you a Computer / IT professional? Join Tek-Tips now!
- Talk With Other Members
- Be Notified Of Responses
To Your Posts
- Keyword Search
- One-Click Access To Your
Favorite Forums
- Automated Signatures
On Your Posts
- Best Of All, It's Free!
*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.
Partner With Us!
"Best Of Breed" Forums Add Stickiness To Your Site

(Download This Button Today!)
Feedback
"...Thank you for the best reply I've ever had to a forum question - it's extremely comprehensive and legible and answers my query thoroughly..."
Geography
Where in the world do Tek-Tips members come from?
|
File Storage
|
What are INI files and how do I interact with them in Delphi?
Posted: 6 Feb 12 (Edited 15 Feb 12)
|
In this example, I not only demonstrate the basics of INI Files and working with them, but I also wrap the functionality into its own wrapper. This enables you to encapsulate all fields of the INI file within a common class of your own.
INI File Basics
An INI file is a flat text file which is laid out in a certain standard format. It's used to store light amounts of data, and is quite often used for storing application settings and other various data. It consists of Sections and Values. An INI file can contain a number of unique Sections, and each of those Sections can contain a number of Values. INI Files often use another file extension other than '.ini' for the developer's personalization of their projects.
A Section is enclosed with [ and ] (for example, [MySection]). There cannot be more than one section with the same name within an INI file.
A Value is defined with an = following its value name, and then the actual value after that. Each Value is on its own line, underneath a Section.
There can be any amount of empty lines anywhere within the file - you do not need to keep any certain amount of line breaks.
You can use the same Value Names in different Sections, but not within the same Section.
Here's a sample INI file...
CODE[Settings] Width=200 Height=100 Left=0 Top=0 Background=1 [Files] Background=C:\SomeFile.jpg OkButton=C:\OKButtonFile.jpg CancelButton=C:\CancelButtonFile.jpg Delphi INI
In Delphi, you use the TIniFile (uses IniFiles unit). This object encapsulates all the INI file functionality for you. The constructor requires the path/filename of the file you would like to work with. If the path specified is invalid (illegal path), or if the path is on a read-only device, then it will fail. If the file does not exist, and it's on a valid path, then the file will automatically be created.
The TIniFile automatically saves the file. However there's another object called the TMemIniFile which I will not go into detail about, but will say that a) it does not automatically save, and b) it keeps its info in the memory until written. The TMemIniFile can be useful if you want to a) Directly read/write to/from the INI file object, AND b) implement saving on demand or even an undo option.
When creating a TIniFile instance, you should enclose it in a try..finally block...
CODEprocedure TForm1.Button1Click(Sender: TObject); var I: TIniFile; S: String; D: TDateTime; begin S:= 'Hello World!'; D:= Now; I:= TIniFile.Create('C:\MyIniFile.ini'); try I.WriteString('MySection', 'MyString', S); I.WriteDateTime('MySection', 'DateTime', D); finally I.Free; end; end; It's not absolutely necessary to be in a try..finally block, especially if you plan on wrapping it in a common class (see example further below).
And then when you read from it...
CODEprocedure TForm1.Button2Click(Sender: TObject); var I: TIniFile; S: String; D: TDateTime; begin I:= TIniFile.Create('C:\MyIniFile.ini'); try S:= I.ReadString('MySection', 'MyString', 'Default Value'); D:= I.ReadDateTime('MySection', 'DateTime', Now); finally I.Free; end; end; Notice the third parameter in the read functions... This is the default value. If for any reason the value cannot be read, if either it does not exist or if the file is invalid, then the reading function will return this default value instead.
INI Section Lists
You can store a list of sections within an INI file. Although the TIniFile doesn't come equipped with any Section List capabilities, you can still use some things in it to encapsulate your own list.
Take this file for example...
CODE[Settings] SomeProp=Yes AnotherProp=No [Item:0] Name=This is Item 0 Val1=1 Val2=2 [Item:1] Name=This is Item 1 Val1=3 Val2=4 [Item:2] Val1=5 Val2=6 As you see, I'm using a number of sections with their names all 'Item:' + the index. You can start your index from 0 or 1, up to you, but 0 is common and easier. It is not necessarily a requirement to have every single value under each item section (see example below).
In Delphi, things start getting a little interesting. Now you need to use a TStringList to navigate through your section names when reading them...
CODEprocedure TForm1.Button3Click(Sender: TObject); var I: TIniFile; L: TStringList; X: Integer; S: String; Z: Integer; T: String; begin I:= TIniFile.Create('C:\MyIniFile.ini'); try L:= TStringList.Create; try I.ReadSections(L); for X:= 0 to L.Count - 1 do begin S:= LowerCase(L[X]); if Pos('item:', S) = 1 then begin T:= S; Delete(T, 1, 5); //Length of 'item:' Z:= StrToIntDef(T, 0); //True index of item ListBox1.Items.Append('Item '+IntToStr(Z)+': '+I.ReadString(S, 'Name', '(No Name)')); end; end; finally L.Free; end; finally I.Free; end; end; TIniFile.ReadSections() is the magic in this example.
I assume saving a section list in this format should be self explanatory, just reverse-engineer it - do a loop of saving those section names. However, I must also advise that when re-writing an existing file with existing list items, you must first clear all the old ones before saving the new ones - mainly to avoid possible duplicating indexes and stray sections that you don't want to stay. This can be done by using 'EraseSection()' and just provide the name of the section you want erased, along with all its values. You might even want to delete the file and re-write it entirely - it's up to you.
INI Value Lists
Just like the above example for storing Section Lists, you can use a similar approach for storing Value Lists.
CODEprocedure TForm1.Button4Click(Sender: TObject); var I: TIniFile; L: TStringList; X: Integer; S: String; Z: Integer; T: String; begin I:= TIniFile.Create('C:\MyIniFile.ini'); try L:= TStringList.Create; try I.ReadSectionValues('MySection', L); for X:= 0 to L.Count - 1 do begin S:= LowerCase(L[X]); if Pos('item:', S) = 1 then begin T:= S; Delete(T, 1, 5); //Length of 'item:' Z:= StrToIntDef(T, 0); //True index of item ListBox1.Items.Append('Item '+IntToStr(X)+': '+I.ReadString('MySection', S, '(No Name)')); end; end; finally L.Free; end; finally I.Free; end; end;
Encapsulating INI File
Now, I will demonstrate how to put all that together into an object with properties.
CODEtype TMyIniFile = class(TObject) private FFilename: String; FActive: Bool; FIni: TIniFile; function GetMyString: String; function GetMyInteger: Integer; procedure SetMyString(const Value: String); procedure SetMyInteger(const Value: Integer); public constructor Create(const Filename: String); destructor Destroy; override; published property MyString: String read GetMyString write SetMyString; property MyInteger: Integer read GetMyInteger write SetMyInteger; end;
implementation
constructor TMyIniFile.Create(const Filename: String); begin FActive:= False; FFilename:= AFilename; try FIni:= TIniFile.Create(FFilename); FActive:= True; except on e: exception do begin FIni.Free; end; end; end;
destructor TMyIniFile.Destroy; begin if FActive then FIni.Free; inherited; end;
function TMyIniFile.GetMyString: String; begin if FActive then Result:= FIni.ReadString('MySection', 'MyString', '') else Result:= ''; end;
function TMyIniFile.GetMyInteger: Integer; begin if FActive then Result:= FIni.ReadInteger('MySection', 'MyInteger', 0) else Result:= 0; end;
procedure TMyIniFile.SetMyString(const Value: String); begin if FActive then FIni.WriteString('MySection', 'MyString', Value); end;
procedure TMyIniFile.SetMyInteger(const Value: Integer); begin if FActive then FIni.WriteInteger('MySection', 'MyInteger', Value); end;
Notice how I put protection around it using FActive. This is just extra security just in case something went wrong upon creating it. There are a number of errors that can occur, mainly due to invalid filenames being passed to its constructor.
Now you just create an instance if TMyIniFile (passing a valid filename) and use its properties MyString and MyInteger.
That's it for now, I'll probably add more later. NOTE: This code was 98% typed directly into this website. Sorry and please notify me if you find any problems in my sample code.
|
Back to Embarcadero: Delphi FAQ Index
Back to Embarcadero: Delphi Forum |
|
 |
|