Windows umożliwia zapisywanie konfiguracji w specjalnych plikach tekstowych o charakterystycznej budowie. Pliki te mają rozszerzenie .ini i zawierają informacje dotyczące konfiguracji danego programu. Sam możesz się o tym przekonać - chyba najpopularniejszym plikiem INI w Windows jest system.ini. Dzięki temu rodzajowi plików masz możliwość zapisania konfiguracji swojego programu. Jest to wygodny sposób na zapisanie jakiś danych. Wyobraź sobie program, który wymaga od użytkownika podania pewnych danych. Przykładowo będzie to imię. W takim przypadku po każdym uruchomieniu programu użytkownik byłby zmuszony do podania swojego imienia w celu dalszego użytkowania programu. Dzięki plikom INI po wpisaniu imienia na dysku może zostać stworzony plik, który zawierał będzie te informacje. Po następnym uruchomieniu programu będzie on sprawdzał, czy ten plik w określony miejscu istnieje - jeżeli tak odczytuje z niego informacje o imieniu - jeżeli nie istnieje to wyświetla okienko z informacją i wymaga podania.    

Budowa


Budowa pliku INI nie jest skomplikowana. Plik INI można edytować za pomocą dowolnego edytora tekstu - nawet za pomocą notatnika. Każdy plik dzieli się na tzw. sekcje. Za pomocą tych sekcji możesz podzielić plik na poszczególne kategorie (opcje ogólne, konfiguracja, dane itp. ). Każda sekcja natomiast może zawierać klucze i wartości.  

 
[Main]
Login=Bald 
 


Sekcja zawsze wpisywana jest w nawiasie kwadratowym. Wszystkie klucze występujące w pliku wpisywane są pod spodem. Konstrukcja jest taka:

 
klucz=wartość
 


Pliki te mogą także zawierać komentarze. Komentarze mogą opisywać poszczególne wartości - do czego służy dany klucz i jaki efekt można osiągnąć zmieniając wartość danego klucza. Przykładowo w języku skryptowym PHP cała konfiguracja mieści się w pliku INI z którego program czyta potrzebne informacje. Chcąc więc zmienić ustawienia programu trzeba edytować plik php.ini. Żeby wiedzieć co edytować, co zmienić i co do czego służy każda wartość jest skomentowana. Komentować możesz tylko jedną linię za pomocą znaku ; (średnik). Plik .ini może równie dobrze wyglądać tak a program i tak poradzi sobie z jego odczytem:

 
;  Copyright (c) 2002 by Adam Boduch
;
;  Jeżeli chcesz zresetowac ustawienia - usun plik setup.ini z dysku
[Main]
Login=Bald        ; komentarz
 


Zwróć jednak uwagę na jedną rzecz. Między linią konfiguracji (klucza), a komentarzem jest przerwa. Przerwa ta jest konieczna, aby program prawidłowo odczytał dane. Przerwą w tym wypadku musi być tabulator.  


Tworzenie i otwieranie


Na samym początku musisz wiedzieć jedną rzecz. Mianowicie większość plików INI jest trzymana domyślnie w katalogu Windowsa. (np. C:\Windows). Więc tworząc plik INI musisz podać jego pełną ścieżkę... jeżeli oczywiście chcesz ten plik zapisać w innym miejscu niż katalog Windowsa. Nasuwa się więc pytanie jak pobrać np. ścieżkę naszego uruchomionego programu. Chyba nie będzie z tym problemu. Kompletną ścieżkę programu podaje funkcja ExeName, z klasy TApplication. Czyli:

 
S := Application.ExeName;
 


Zmienna S zawiera teraz ścieżkę do uruchomionego programu - np: C:\Moje programy\2\moj_program.exe. Teraz nasuwa się pytanie jak z tej wartości wyciągnąć tylko ścieżkę programu? Nie ma problemu. Do tego VCL udostępnia nam parę przyjemnych funkcji. (patrz. tabela poniżej).

FunkcjaOpis
ExtractFileDirFunkcja zwraca katalog pliku bez jego nazwy i nie zakończoną znakiem "".
ExtractFilePathZwraca kompletny katalog pliku ze znakiem "" na końcu.
ExtractShortPathNameFunkcja zwraca skróconą nazwę pliku - np: :\Progra~1\Borland\Delphi\Bin\Delphi32.exe
ExtractFileDriveFunkcja zwraca jedynie literę dysku na której znajduje się plik.
ExtractFileNameFunkcja zwraca z podanej ścieżki jedynie nazwę pliku.
ExtractFileExtTa funkcja natomiast podaje jedynie rozszerzenie z podanej w parametrze ścieżki.
ChangeFileExtFunkcja zmienia rozszerzenie pliku.


Do operowania na plikach INI Delphi udostępnia nam (jak by inaczej...) wygodną klasę TINIFile. Żeby skorzystać z tej klasy należy do modułu uses dodać słowo INIFiles włączając w ten sposób moduł INIFiles do projektu.
Na sam początek, aby skorzystać z tej klasy trzeba oczywiście wywołać jej konstruktor - zapis może wyglądać tak:

 
Var
  INI : TINIFile;
begin
  INI := TINIFile.Create(ExtractFilePath(Application.ExeName) + 'setup.ini');
  try
    { jakieś instrukcje }
  finally
    INI.Free;
  end;
end;  
 


W konstruktorze tej klasy należy podać nazwę pliku INI. W tym parametrze podałem ścieżkę gdzie plik ma zostać stworzony.

ExtractFilePath(Application.ExeName) podaje jedynie lokalizację katalogów gdzie znajduje się program. Czyli w rezultacie plik znajdzie się w katalogu {nazwa_katalogu_z_programem}\setup.ini.

Uwaga! Nie podając w tym miejscu pełnej ścieżki katalogu plik INI zostanie stworzony w katalogu z Windowsem.  

Edycja


Klasa TINIFile udostępnia bardzo proste i wygodne funkcje, które służą do odczytu lub zapisu danych. Do pliku INI mogą być zapisane: liczby, tekst, liczby typu Float (zmiennoprzecinkowe), data i czas, wartość typu Boolean oraz całe strumienie danych.  
Nazwy poleceń są intuicyjne bo np. co robi polecenie WriteString ?  Można się tego domyśleć - zapisuje tekst. A funkcja, która ten tekst odczytuje? Nazywa się ReadString. Jak widzisz zamieniamy tylko słowo Write na Read a drugi człon funkcji to typ danych. Zapis nowych wartości do pliku mógłby wyglądać następująco:

 
      INI := TINIFile.Create(ExtractFilePath(Application.ExeName) + 'setup.ini');
      try
        INI.WriteString('Main', 'Login', Login); 
      finally
        INI.Free;
      end;
 


W poleceniu WriteString pierwszy parametr, który musi zostać podany to nazwa sekcji. Nie martw się - jeżeli plik nie istnieje lub nie istnieje taka sekcja w tym pliku - zostanie ona utworzona. Jeżeli natomiast istnieje - zostaną dopisane do niej nowe dane - tym nie musisz zaprzątać sobie głowy. Drugi parametr to nazwa klucza - w tym wypadku klucz nazywać się będzie Login. Ostatni parametr to wartość tego klucza, czyli wartość która będzie przypisana do danego klucza - mam nadzieje, że tutaj jest wszystko jasne.  
A co z odczytem? Zobacz:

 
  INI := TINIFile.Create(ExtractFilePath(Application.ExeName) + 'setup.ini');
  try
    Login := INI.ReadString('Main', 'Login', 'Adam Boduch'); 
  finally
    INI.Free;
  end;
 


Funkcja odczytująca - ReadString także posiada trzy parametry. Pierwszy to nazwa sekcji, z której dane zostaną odczytane. Drugi parametr to nazwa klucza, z którego mają być odczytane dane. Ostatni parametr oznacza wartość domyślną w przypadku, gdy klucz o podanej nazwie nie istnieje lub gdy nie istnieje sam plik INI. Jak już mówiłem polecenie ReadString jest funkcją, czyli zwraca ona rezultat więc ten rezultat musi zostać przypisany do jakieś zmiennej lub np. od razu wyświetlony.  
Na podstawie tych poleceń możemy napisać już jakiś program, który z plików INI będzie korzystał. Np. program, który po uruchomieniu będzie sprawdzał, czy w katalogu istnieje plik setup.ini - jeżeli nie będzie wyświetlał okienko z prośbą o podanie loginu. Po wpisaniu jakiegoś loginu dane zostaną zapisane do pliku INI. Po ponownym uruchomieniu programu dane te będą widniały na formie, a żadne okienko podczas uruchamiania się nie pokaże.  
W przypadku wyświetlania nowego okienka nie musimy się zajmować tworzeniem nowej formy. Moduł Dialogs.pas udostępnia do tego funkcję <vcl>InputBox</vcl>. Ta funkcja zwraca informację wpisaną przez użytkownika w okienku informacyjnym.

Oczywiście my sami możemy dokonać wyboru co ma się znaleźć na belce tytułowej lub wewnątrz tego okna. Można by także było w tym wypadku tworzyć nową formę, ale w tym wypadku trzeba by było oprogramować parę zdarzeń, a wiadomo - programista to człowiek leniwy...
Wywołanie funkcji InputBox może wyglądać tak:

Login := InputBox('Rejestracja...', 'Podaj login', '');


Zmienna Login od tej pory przechowywać będzie informację wpisaną w tym oknie. Pierwszy parametr tego poelcenia to tekst, który wyświetlony zostanie na belce tytułowej. Drugi parametr to tekst, który zostanie wyświetlony na komponencie Label znajdującym się w tym oknie. Ostatni parametr to tekst domyślny w kontrolce edycyjnej Edit. Ja w tym wypadku wpisałem '' co oznacza tekst pusty.
Wygeneruj zdarzenie OnCreate formy i wpisz w niej taki kod:

 
procedure TMainForm.FormCreate(Sender: TObject);
var
  INI : TINIFile;
  Login : String; // login wczytany z pliku INI
begin
{ na samym początku należy sprawdzić, czy plik jest utworzony }
  if not FileExists(ExtractFilePath(Application.ExeName) + 'setup.ini') then
  begin
  { jeżeli nie można znaleźć pliku - wyświetl okienko z informacją z prośbą o
    wpisanie swojego loginu }
    Login := InputBox('Rejestracja...', 'Podaj login', '');
    if Login <> '' then // sprawdź, czy login nie jest pusty
    begin
    { jeżeli tak nie jest - utwórz plik }
      INI := TINIFile.Create(ExtractFilePath(Application.ExeName) + 'setup.ini');
      try
        INI.WriteString('Main', 'Login', Login); // zapisz do pliku login
        lblMain.Caption := 'Witaj ' + Login;
      finally
        INI.Free;
      end;
      Exit;  // nie rób już nic...
    end else Application.Terminate; // jeżeli użytkownik nie wpisał loginu - zakończ aplikacje
  end;
 
 { ten kod zostanie wykonany tylko wtedy, gdy plik INI został znaleziony }
  INI := TINIFile.Create(ExtractFilePath(Application.ExeName) + 'setup.ini');
  try
    Login := INI.ReadString('Main', 'Login', 'Adam Boduch'); // następuje odczyt loginu
    lblMain.Caption := 'Witaj ' + Login; // następnie wyświetl login na komponencie
  finally
    INI.Free;
  end;
end;
 


Przyznam się, że parę warunków if tutaj się znajduje... Użytkownik w oknie może przecież równie dobrze wpisać pusty tekst więc trzeba się przed tym zabezpieczyć i wykonać kod pod warunkiem iż tekst wpisany w kontrolce będzie różny od '', czyli pustego łańcucha. Ten kod zostanie wykonany w przypadku, gdy plik w katalogu z programem się nie znajduje (funkcja FileExists).

PolecenieOpis
ReadBinaryStream/WriteBinaryStreamOdczytuje (ReadBinaryStream) i zapisuje WriteBinaryStream) z pliku dane w postaci strumienia. Trzeci parametr musi być zmienną typu TStream
ReadBool/WriteBoolOdczytuje i zapisuje dane typu Boolean, czyli TRUE lub FALSE.
ReadDate/WriteDateOdczytuje (ReadDate) i zapiusuje (WriteDate) wartość typu TDate.
ReadDateTime/WriteDateTimeOdczytuje i zapisuje dane w postaci DateTime, czyli datę i czas.
ReadTime/WriteTimeOdczytuje i zapisuje w pliku jedynie czas zmienna typu TTime).
ReadFloat/WriteFloatOdczytuje i zapisuje dane zmiennoprzecinkowe, czyli typ typu Double.
ReadInteger/WriteIntegerOdczytuje i zapisuje liczby (typ Integer).
ReadString/WriteString Zapisuje i odczytuje tekst (typ String).


W tabeli powyżej przedstawiono wszystkie funkcje służące do zapisywania i odczytywania danych różnych typów z pliku INI. Funkcja ReadBinaryStream/WriteBinaryStream jest nowością w Delphi 6.

Usuwanie kluczy


Klasa TINIFile umożliwia także odczytywanie nazw sekcji lub wszystkich kluczy zawartych w sekcji. Także usuwanie nie powinno sprawić problemu, gdyż są do tego odpowiednie funkcje.
Odczytywanie wszystkich wartości w sekcji następuje za pomocą procedury ReadSections. Procedura ta odczytuje wszystkie sekcje w pliku INI do zmiennej typu TStrings. Czyli kod odczytania wszystkich sekcji mógłby wyglądać tak:

 
INI := TINIFile.Create('setup.ini');
Sections := TStringList.Create;
Try
  INI.ReadSections(Sections);
finally
  INI.Free;
end;
 


Po wykonaniu takiego kodu zmienna Sections zawierać będzie wszystkie sekcje znajdujące się w danym pliku.  W tym momencie każda linia zmiennej Sections to jedna sekcja tego pliku. Odczytać ją można np. tak:

 
ShowMessage(
  Sections[0]
);
 


Tutaj odczytujemy pierwszą linię zmiennej, czyli pierwszą sekcję.

Uwaga! W przypadku próby odczytania linii która to nie znajduje się w zmiennej wyświetlone zostanie okienko z błędem. Jeżeli np. linii w zmiennej jest 10, a Ty próbujesz odczytać 11 program wyświetli błąd: "List index out of bounds [11]".  
Równie dobrze zamiast tworzyć nowy obiekt typu TStrings można od razu odczytać sekcję do komponentu - np. TListBox:

 
procedure TMainForm.FormCreate(Sender: TObject);
var
  INI : TINIFile;
begin
  INI := TINIFile.Create('win.ini');  // odczytaj plik
  try
    INI.ReadSections(lblSections.Items); // wyświetl w obiekcie wszystkie sekcje
  finally
    INI.Free;
  end;
end;
 


lblSections to w tym wypadku nazwa komponentu typu ListBox. Po kliknięciu na daną pozycję w tym komponencie można odczytać wszystkie wartości znajdujące się w tej sekcji. Wygeneruj w tym celu zdarzenie OnClick komponentu ListBox, a następnie wpisz:

 
procedure TMainForm.lblSectionsClick(Sender: TObject);
var
  INI : TINIFile;
begin
  INI := TINIFile.Create('win.ini');
  try
    lbSection.Caption := 'Sekcja: ' + lblSections.Items[lblSections.ItemIndex];
    { odczytaj do komponentu wartości wszystkich sekcji }
    INI.ReadSectionValues(lblSections.Items[lblSections.ItemIndex], lblSection.Items);
  finally
    INI.Free;
  end;
end;
 


Klasa udostępnia także procedurę ReadSections, które to odczytuje jedynie nazwy kluczy w danej sekcji. Pierwszym parametrem w tej procedurze musi być nazwa sekcji do odczytania, a drugi parametr to zmienna typu TStrings. Ja użyłem tutaj procedurę ReadSectionsValues, która odczytuje nie tylko nazwy kluczy, ale także wartości w danym kluczu zawarte.
Komponent ListBox posiada właściwość ItemIndex określającą numer pozycji w która jest aktualnie zaznaczona. Korzystając z tej wartości można pobrać tekst zaznaczonej w tym komponencie pozycji. W przypadku, gdy napiszesz tak:

lblSections.Items[0];


Odczytujesz pierwszą pozycję w komponencie, a żeby odczytać zaznaczoną pozycję należy w tym miejscu podstawić wartość właściwości ItemIndex, czyli całość może wyglądać tak:

 
lblSections.Items[lblSections.ItemIndex]


Jak to to teraz usunąć? Jeżeli chcesz usunąć całą sekcję należy wywołać procedurę EraseSection podając w parametrze nazwę sekcji - np:

INI.EraseSection('Moja sekcja');


Po takim zabiegu wszystkie klucze z danej sekcji zostaną usunięte. Możesz także usunąć pojedynczy klucz wywołując procedurę DeleteKey. Ta procedura musi jedynie zawierać w parametrze nazwę klucza do usunięcia - to wszystko!

.NET

Od Delphi 8 pliki INI już nie są w tak powszechnym użyciu i ich używanie nie jest zalecane. Zamiast tego zalecane jest używanie plików XML do przechowywania danych.

Kategoria: Delphi » Artykuły

23 komentarze

Avatar: TomRiddle
Napisany 2011-04-01 16:57 przez TomRiddle

Jezu, TColor to Integer. Zrub sobie kiedyś tak:
var
  I : Integer;
  C : TColor;
begin
 C := Form1.Color;
 I := C;
end;

Pewnie pomyślisz żę wywali błąd
Incopatible types "Tcolor" and "Integer", a tu zonk. Zadziała dobrze.

Ps; możesz nawet looknąć na funkcję RGB;
Zwraca wynik w Integer;

Avatar: programik3
Napisany 2008-03-11 15:27 przez programik3

... lub WriteColor:

if Label1.Color = clBlue then
begin
PlikINI.WriteString('Sekcja','ZapisanyKolor','Blue');
end;

:P

Avatar: programik3
Napisany 2008-03-11 15:23 przez programik3

Mozna zrobic ReadColor IFem

if PlikINI.ReadString('Sekcja','Kolor','') = 'White' then
begin
Label1.Color := clWhite;
end;

Avatar: programik3
Napisany 2008-03-04 15:21 przez programik3

Jesli to Integer to jestem eukaliptus....

Avatar: Johny_Morfina
Napisany 2007-08-02 09:45 przez Johny_Morfina

TColor to przeciez Integer... nie?

Brak avatara
Napisany 2007-08-02 08:16 przez Morgoth_

FILL ->
"    INI := TINIFile.Create(ExtractFilePath(Application.ExeName) + 'program.ini');
    try
      Form1.Left := INI.ReadInteger('00', 'X', 826);
      Form1.Top := INI.ReadInteger('00', 'Y', 0);
      Form1.Width := INI.ReadInteger('00', 'Xsize', 199);
      Form1.Height := INI.ReadInteger('00', 'Ysize', 488);
    finally
      INI.Free;
    end;
"

Odpowiedź na Twoje pytanie dużo niżej.

tomalla ->
"INI.WriteString('sekcja','klucz',Memo1.Lines.<b><font color="red">Text</font></b>);"

Mi się podoba ten artykuł.

:P

Procek ->

"ExtractFilePath(Application.ExeName);"

Mam pytanie. Jak zapisać kolor do pliku INI. ReadColor nie istnieje. Nie ma czegoś takiego jak ColorToStr :/

???

Brak avatara
Napisany 2007-07-22 00:27 przez Arthan

@mr_Zola
a co za różnica czy Windows czy biblioteki? Istnieje jakiś format plików czy to mp3 czy doc i są programy je obsługujące ja podobnie postrzegam pliki ini.. czyżbym się mylił ?? :P
@tomalla
"ReadColor" nie ma .. ale możesz sama napisać ;P
@procek
raczej nie lepiej... bo zwraca aktualny katalog nie katalog programu, więc np. odpalając ten program w dosie lub zmieniając w skrócie właściwość "Rozpocznij w" zwróci inną ścieżkę

Brak avatara
Napisany 2007-01-02 16:52 przez procek

>>A mi nie działa \'Application.ExeName\', ale wpisalem \'ParamStr(0)\' i jest :P

A nie lepiej GetCurrentDir ??

Brak avatara
Napisany 2006-10-25 21:23 przez tomalla

A mam jeszcze jedno pytanko - Czy jest coś w stylu ReadColor? Otóż chcę żeby mi wczytał kolor z pliku INI. Na przykład:
...
Form1.Color := INI.Read...?...('General', 'Color', clBtnFace);
...

Proszę o szybką odpowiedź

Brak avatara
Napisany 2006-06-13 08:21 przez mr_Zola

"Windows umożliwia zapisywanie konfiguracji w specjalnych plikach tekstowych o charakterystycznej budowie..."
czy to windows umożliwia czy może jakaś biblioteka?

Brak avatara
Napisany 2006-06-04 19:29 przez Adam Boduch

Ja tam zadnej tabelki nie widze :P
W kazdym razie tutaj (http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_6) masz bardziej rozbudowany tekst o INI. Moze Ci pomoze.

Brak avatara
Napisany 2006-06-03 11:39 przez tomalla

jest tu tabelka ze zmiennymi, jakie można wpisać do tego ini, np. writeinteger itd, ale jak można tam wpisać zmienną tstring? dokładniej z memo?!?!

Brak avatara
Napisany 2006-01-02 08:14 przez keinxor

odswiezam art jak dla mnie super pomocny Panie Adamie tak trzymac!!!

Brak avatara
Napisany 2005-08-07 22:47 przez explosive

A mi nie działa \'Application.ExeName\', ale wpisalem \'ParamStr(0)\' i jest :P

Avatar: WeeR
Napisany 2005-04-28 07:00 przez WeeR

Snowak: no to sie troche spóźniełeś.... jakieś 2 lata

Avatar: Mnich TT
Napisany 2004-04-18 12:34 przez Mnich TT

:/

Brak avatara
Napisany 2004-04-24 17:20 przez Snowak

buuuuuuuu! A ja wlasnie chcialem o tym arta napisac!

Avatar: slave_k
Napisany 2003-03-23 11:28 przez slave_k

no...<ort>artykół</ort> nie taki zły.
Dzienki temu <ort>artykółowi</ort>, moje progi mają ustawienia, własciwości, konfiguracje....
Tylko nie wiem czy to ten sam co kiedyś czytałem, bo w tamtym była tabelak z możliwościami innymi niż tylko WRITE i READ strin i integer.
Dużo też sam doszedłem, ale bardzo szybko.
Wystarczy zrozumieć o co chodzi i ze wyszystkim innym (readbool, i inne) jest tak samo.

Brak avatara
Napisany 2003-03-12 13:29 przez Adam Boduch

Artykul do bani. Jezuuu, ja to pisalem????

Brak avatara
Napisany 2003-02-16 20:26 przez FILL

W której procedurze wczytać położenie formy wcześniej zapisane w ini?

Brak avatara
Napisany 2002-12-16 15:10 przez Adam Boduch

'01' to nazwa klucza - w programie nie ma to takiego znaczenia, gdyz moze byc jakakolwiek.

Brak avatara
Napisany 2002-12-16 10:24 przez sad

czy w pliku muszą być '01' itd. ?

Avatar: korn
Napisany 2002-11-18 18:17 przez korn

a jak przeszukac plik, jesli zawiera w sobie d*pa=d*psko to zeby odczytalo d*psko do zmiennej ?? napisz na szkola81@go2.pl
narq

4programmers.net