INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Log In

Come Join Us!

Are you a
Computer / IT professional?
Join Tek-Tips Forums!
  • 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.

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.

Jobs

File Storage

What are INI files and how do I interact with them in Delphi? by djjd47130
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...

CODE

procedure 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...

CODE

procedure 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...

CODE

procedure 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.

CODE

procedure 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.

CODE

type
  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

My Archive

Resources

Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close