Umíme to s Delphi: 68. díl – práce se soubory *.ini

Dnešní, předsilvestrovské vydání seriálu, volně navazuje na předchozí část, ale především reaguje na přání řady čtenářů, kteří vyjádřili touhu proniknout do způsobu práce se soubory *.ini. Dnešní díl ukáže teoreticky i prakticky, jak na to. Uvidíte, že uchovávat uživatelská nastavení a další údaje je nejen velmi efektní a profesionální, ale také nadmíru jednoduché.
Než se dostaneme k praxi, pokusíme se (jak je naším zvykem) provést jakousi teoretickou disputaci, která nás seznámí s principem a důvody používání souborů *.ini (a systémových registrů).

Představme si běžnou situaci: uživatel spustí novou aplikaci a v průběhu práce provede celou řadu (mnohdy bezděčných) činností: nastaví jinou pozici, velikost, barvu nebo jiné atributy okna; změní výchozí nastavení adresářů, cest, přípon; nastaví si svou vlastní preferovanou tiskárnu nebo jakoukoliv jinou konfiguraci... ve všech těchto všech případech se může aplikace v okamžiku ukončení zachovat v podstatě třemi základními způsoby:

  • vše zapomenout a při následujícím spuštění nabídnout opět „default“ nastavení. Taková aplikace ovšem nemůže pomýšlet na nehynoucí slávu a obdiv uživatelův.
  • údaje uchovat nějakým „soukromým“ způsobem definovaným přímo programátorem, například v textovém nebo binárním souboru, který by byl vždy po spuštění aplikace načten a před ukončením aplikace naopak aktualizován. Tento způsob je z uživatelského hlediska samozřejmě bezproblémový (uživateli je jedno, jak a kde si aplikace pamatuje jeho přání, hlavně, že se tak stane), nicméně pro programátora znamená zbytečnou práci navíc. Musel by totiž definovat formát, vytvořit soubor a implementovat metody pro čtení a zápis údajů z/do tohoto souboru. Možné, jednoduché, avšak zbytečně pracné.
  • údaje uchovat za využití nějakého obecně fungujícího mechanismu. Z pohledu uživatele nedochází k žádné změně oproti předchozímu bodu, údaje a nastavení se prostě „neztratily“. Znavené oko vývojáře však může radostně spočinout na řadě významných ulehčení: není třeba definovat žádné formáty, prostředky ani procesy pro ukládání a čtení údajů. Poněkud zjednodušeně řečeno – jediné, co musí programátor udělat, je rozhodnout se, že chce daný mechanismus použít a stanovit údaje, které hodlá ukládat.
Je asi zbytečné dodávat, že se v dnešním článku budeme zabývat právě třetí uvedenou cestou. Pojďme však v úvahách dále. Dejme tomu, že jsme se rozhodli použít třetí způsob. Pak musíme provést ještě jednu volbu. Můžeme totiž používat dva druhy „standardního“ mechanismu pro uchovávání údajů a nastavení:
  • ukládání provádět na systémové úrovni. Výhodou je naprostá jednoduchost; stačí jen a pouze říci, čeho se údaje týkají a kam by měly být uloženy. Vše ostatní, včetně stanovení fyzického umístění a realizace uložení ponecháme na operačním systému. Nevýhodou tohoto přístupu je závislost na systému: naše aplikace bude pracovat (resp. půjde přeložit) jen v tom systému, který disponuje použitým mechanismem.
  • ukládání provádět na obecnější úrovni, která nepoužívá žádných specialit operačního systému, ale „vystačí si“ se soubory. Výhodou je samozřejmě nezávislost na systému, mezi nevýhodami můžeme zmínit možné omezení velikosti souborů a menší možnosti než u prvního uvedeného způsobu.
Ačkoliv jsem se pokoušel být v předchozím odstavci nanejvýše obecný, všichni čtenáři asi vytušili, kam vítr vane. Prvním, systémově orientovaným způsobem jsou samozřejmě systémové registry (použitelné pouze v aplikacích systému Windows), druhý pak představují soubory *.ini (použitelné na všech platformách).

V následujících podkapitolách dnešního dílu se začneme zabývat obecným řešením – prací se soubory *.ini. Ani systémové registry nám však nezůstanou utajeny – počkáme si však na ně do příštího týdne. Vrcholem našeho snažení pak bude seznámení s jakýmsi „hybridem“ pracujícím, ač se to zdá nemožné, zároveň oběma uvedenými způsoby.

Inicializační soubory (*.ini)

Jak už bylo uvedeno, soubory *.ini patří už dlouhou dobu ke standardnímu způsobu uchovávání uživatelských nastavení a údajů. V poslední době je sice patrný trend uchovávat tyto údaje „bezpečně zašité“ v registru Windows (hovoříme samozřejmě o windowsových aplikacích), nicméně inicializační soubory budou jistě používány ještě dlouhou dobu.

V Delphi je práce s nimi mimořádně snadná. Vystačíte-li si se základní funkčností, budete potřebovat pouze třídu TIniFile, která zapouzdřuje soubor *.ini i se všemi operacemi, které s ním můžeme provádět. Pojďme se nejprve podívat na jednoduchou ukázku, pak si uvedeme více podrobností.

Vytvoříme novou aplikaci a zařídíme, aby aplikace po svém startu načetla z inicializačního souboru jednak informace o poloze a velikosti okna a jednak obsah editačního pole. Tytéž údaje budou při ukončení aplikace opět uloženy.

1. Vytvořte novou aplikaci, na hlavní formulář (Form1) umístěte jedno tlačítko (Button1) a jedno editační pole (Edit1). Tlačítko bude sloužit pouze k ukončení aplikace.

2. Nejprve je nutné přidat de sekce Uses jednotku IniFiles, neboť třída TIniFile je definována právě v této jednotce:

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, IniFiles;

3. Dále ošetříme událost OnClick tlačítka Button1:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Form1.Close; { :-))) }
end;

4. Nyní ošetříme událost OnCreate hlavního formuláře:

procedure TForm1.FormCreate(Sender: TObject);
var IniSoub: TIniFile;
begin
  Form1.Caption:= `Ukazka TIniFile`;
  Edit1.Text := ``;
  Button1.Caption := `&Konec`;

  IniSoub := TIniFile.Create(ChangeFileExt(Application.ExeName, `.INI`));
  try
    Edit1.Text := IniSoub.ReadString(`Edit`, `Text`, ``);
    Form1.Top := IniSoub.ReadInteger(`Form`, `Top`, 100);
    Form1.Left := IniSoub.ReadInteger(`Form`, `Left`, 100);
    Form1.Height := IniSoub.ReadInteger(`Form`, `Height`, 97);
    Form1.Width := IniSoub.ReadInteger(`Form`, `Width`, 225);
    if IniSoub.ReadBool(`Form`, `Maximized`, false) then
      WindowState := wsMaximized
    else
      WindowState := wsNormal;
  finally
    IniSoub.Free;
  end;
end;

Podrobnosti o jednotlivých metodách a jejich parametrech si uvedeme níže. Nejdůležitější poznatky z této implementace by měly být asi takové: parametrem konstruktoru TIniFile.Create je jméno souboru *.ini (ChangeFileExt používáme jen proto, aby soubor *.ini měl vždy stejný název jako spustitelný soubor aplikace). Pro načítání jednotlivých hodnot ze souboru pak používáme funkce ReadInteger, ReadString a ReadBool, přičemž jejich prvním parametrem je vždy název sekce (viz níže) v souboru, druhým je jméno konkrétní hodnoty a třetím je default hodnota pro případ, že se načtení nepodařilo (např. neexistuje soubor *.ini, neexistuje v něm zadaná sekce nebo neexistuje požadovaný údaj).

5. Ošetříme událost OnClose hlavního formuláře. V ní naopak všechny potřebné údaje uložíme do souboru:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var IniSoub: TIniFile;
begin
  IniSoub := TIniFile.Create(ChangeFileExt(Application.ExeName, `.INI`));
  try
    IniSoub.WriteString(`Edit`, `Text`, Edit1.Text);
    IniSoub.WriteInteger(`Form`, `Top`, Form1.Top);
    IniSoub.WriteInteger(`Form`, `Left`, Form1.Left);
    IniSoub.WriteInteger(`Form`, `Height`, Form1.Height);
    IniSoub.WriteInteger(`Form`, `Width`, Form1.Width);
    IniSoub.WriteBool(`Form`, `Maximized`, WindowState = wsMaximized);
  finally
    IniSoub.Free;
  end;
end;

Opět si uvedeme jen minimum nejdůležitějších informací:

  • údaje zapisujeme pomocí funkcí WriteString, WriteInteger, WriteBool. První dva parametry jsou shodné jako u Read-funkcí, třetím parametrem je pak samozřejmě zapisovaná hodnota;
  • v této funkci (i ve FormCreate) používáme mechanismu výjimek pro ošetření práce se soubory *.ini. Nutno ovšem podotknout, že v případě neexistence souboru, sekce nebo údaje bude v případě čtení vrácena default hodnota a v případě zápisu automaticky vytvořen nový soubor/sekce/údaj, takže výjimka by nastala skutečně pouze u jiných, závažných chyb.

6. Aplikace je hotova. Přeložte a opakovaně spusťte – uvidíte, jak se vše bude jevit profesionálnější.

Klepněte pro větší obrázek

Závěrem příkladu si ještě ukažme, jak vypadá obsah vytvořeného inicializačního souboru:

[Edit]
Text=Nazdar, jak se vede?

Top=100
Left=100
Height=119
Width=246
Maximized=0

Je dobře patrné, že názvy sekcí jsou uvedeny v [hranatých závorkách] a jednotlivé řádky pak mají tvar Jmeno=Hodnota. Pokud jste četli předchozí část našeho seriálu, možná vám tento tvar něco připomíná; ano, jedná se přesně o formát, který podporuje komponenta ValueListEditor, jíž je předchozí díl věnován.

Práce s inicializačními soubory

Po krátkém praktickém příkladu si popíšeme možnosti práce s inicializačními soubory podrobněji. Především je nutné uvést, že máme k dispozici dvě třídy, které jsou zdánlivě podobné, přece jen se však zásadně liší.

Jedná se o třídy TIniFIle a TMemIniFile. Zatímco první z nich, jak jsme si ukázali v předchozím příkladu, pracuje přímo se souborem na disku (všechna volání funkcí WriteXXX způsobí bezprostřední zápis na disk), třída TMemIniFile ukládá všechny údaje pouze do vyrovnávací paměti („bufferuje“ je) a nezapisuje nic na disk až do okamžiku, kdy zavoláme metodu UpdateFile. Důvodem je snaha o minimalizaci přístupu k disku (podrobnosti v tabulce v další podkapitole). Jinak se ovšem TIniFile a TMemIniFile nijak neliší, pracuje se s nimi zcela analogicky a používají se stejné metody. Přestože se v našem seriálu nezabýváme programováním v Kylixu a vůbec jiných aplikací než pro systém Windows, poznamenáme, že v Linuxu se třídy TIniFile a TMemIniFile neliší vůbec; obě pracují přímo s diskem a TMemIniFile operace nebufferuje.

V okamžiku, kdy vytváříme objekt třídy TIniFile nebo TMemIniFile, předáme konstruktoru v parametru název souboru *.ini. Pokud zadaný soubor neexistuje, je automaticky vytvořen. Následně je možné číst jednotlivé údaje metodami ReadString, ReadDate, ReadInteger, ReadBool a dalšími. Je ovšem také možné přečíst najednou celou sekci. K tomu se používá metoda ReadSection.

Třídy TIniFile a TMemIniFile

Pojďme se nyní zaměřit na třídy TIniFile a TMemIniFile. V předchozím odstavci jsme si uvedli, že práce s nimi je naprosto stejná (až na metodu UpdateFile), proto si je popíšeme společně.

Nejdůležitější poznámka k oběma třídám spočívá v tom, že jsou definovány v modulu IniFiles. Pokud tedy chceme v našich programech pracovat se soubory *.ini (resp. využívat zmíněných tříd), je nutné vložit do sekce Uses tento modul.

Obě třídy mají pouze dvě vlastnosti: CaseSensitive a FileName. První z nich je logického typu a udává, mají-li se názvy sekcí a údajů zpracovávat s ohledem na velikost písmen (jinak řečeno, značí-li údaje LEFT a Left totéž). Vlastnost FileName je pouze pro čtení a obsahuje název souboru *.ini, s nímž pracujeme.

Než se zaměříme na metody obou tříd, uvedeme stručnou poznámku týkající se hierarchie tříd: celá hierarchie tříd souvisejících s inicializačními soubory je takováto:

TObject – TCustomIniFile – TMemIniFile – TIniFile

Zatímco ve třídě TMemIniFile jsou definovány metody pro čtení a zápis tak, aby pracovaly s pamětí (nikoliv s diskem), ve třídě TIniFile platí opak. Z hlediska programátora však nenalézáme prakticky žádné změny; obě skupiny metod se používají zcela stejně.

Metoda Popis
procedure DeleteKey(const Section, Ident: String) Odstraní hodnotu z *.ini souboru. V parametrech se zadá sekce a údaj (např. Form a Edit) a ze souboru dojde k vymazání aktuální hodnoty. Neexistuje-li sekce nebo údaj, nedojde k chybě, ale neprovede se jednoduše nic.
procedure EraseSection(const Section: String) Odstraní celou sekci (zadanou jejím názvem) ze souboru *.ini.
procedure ReadSection (const Section: String; Strings: TStrings) Přečte názvy údajů (nikoliv hodnoty) z celé sekce ze souboru *.ini. Přečtené názvy jsou uloženy do objektu Strings, kterým může být buď vlastnost nějaké komponenty (např. ListBox) nebo třeba ručně vytvořený objekt třídy TStringList, v němž hodláme názvy uchovat.
procedure ReadSections(Strings: TStrings) Přečte názvy všech sekcí ze souboru *.ini. Pro ukládání názvů (objekt Strings) platí totéž, co pro metodu ReadSection.
procedure ReadSectionValues(const Section: String; Strings: TStrings) Komplementární metoda k ReadSection; přečte hodnoty (nikoliv názvy) ze všech údajů zadané sekce. Pro ukládání hodnot (objekt Strings) platí totéž, co pro metodu ReadSection.
function ReadString(const Section, Ident, Default: String): String Ze sekce zadané parametrem Section přečte údaj, jehož název je zadaný parametrem Ident a vrátí jej. Pokud zadaná sekce nebo údaj neexistuje nebo pokud příslušný údaj nemá hodnotu v *.ini souboru uvedenu (uloženu), je vrácena hodnota specifikovaná parametrem Default.
procedure WriteString(const Section, Ident, Value: String); Do sekce zadané parametrem Section zapíše údaj, jehož název je předán parametrem Ident, a zapíše hodnotu tohoto údaje, která je předána parametrem Value. Pokud sekce nebo údaj neexistují, jsou založeny.

Další metody použitelné pro objekty obou tříd TIniFile i TMemIniFile jsou analogické s uvedenými: pro čtení hodnot jiných datových typů se používají metody ReadBool, ReadDate, ReadInteger, ReadDateTime, ReadFloat a ReadTime. Pro zápis obdobně WriteBool, WriteDate, WriteDateTime, WriteFloat, WriteInteger a WriteTime. Potřebujeme-li zjistit, zda sekce nebo údaj existují, můžeme použít SectionExists, resp. ValueExists. Poslední dvě funkce umožňují přečíst binární hodnotu a uložit ji do streamu, resp. hodnotu ze streamu uložit do souboru *.ini (streamy jsem se však doposud v seriálu nezabývali): ReadBinaryStream, resp. WriteBinaryStream.

Za týden budeme pokračovat přesně v místě, v němž nyní končíme. Popíšeme si podrobně metodu UpdateFile (která má překvapivě význam nejen pro objekty TMemIniFile) a podíváme se na několik metod, které jsou k dispozici výhradně pro třídu TMemIniFile.

Na závěr

Tolik pro dnešek k popisu práce s inicializačními soubory. Protože jsme opět do dnešního článku nedokázali vměstnat ani zdaleka všechny potřebné informace, budeme pokračovat opět za týden. Nejprve se stručně zaměříme na několik málo „specialit“ třídy TMemIniFile, a pak se vrhneme na další možnosti uchovávání uživatelských a jiných údajů – na registry systému Widnows. Tedy – pokud se všichni za týden setkáme. Buďte na Silvestra opatrní...
Diskuze (10) Další článek: Příští Invex – směr business, jednoznačně

Témata článku: Software, Windows, Programování, Editační funkce, První parametr, Form, Celý disk, Díl, Systémový disk, První funkce, Stejná metoda, Práce, První uvedení, Filename, Hlavní nevýhoda, Třetí způsob, Editační možnosti, Předchozí odstavec, Soubor, Naprostá jednoduchost, Stejný závěr, První případ


Určitě si přečtěte

Apple ukázal novinky: iPad Pro má překonat notebooky a vrací se Mac mini!

Apple ukázal novinky: iPad Pro má překonat notebooky a vrací se Mac mini!

** Apple v New Yorku představoval počítačové novinky ** iPad Pro prý zatočí s notebooky ** Dočkali jsme se také návratu Mac mini

Jakub Čížek | 81

Portál občana už funguje. Na státní web vypadá až překvapivě použitelně

Portál občana už funguje. Na státní web vypadá až překvapivě použitelně

** Portál občana už funguje, vyřídíte na něm první požadavky ** Funkce se budou postupně rozšiřovat ** Web je docela moderní a přehledný

David Polesný | 66

Byli tam! Důkazy o přistání na Měsíci, Lunochody i čínská sonda jsou vidět z vesmíru

Byli tam! Důkazy o přistání na Měsíci, Lunochody i čínská sonda jsou vidět z vesmíru

** Sonda LRO pořídila z oběžné dráhy Měsíce zajímavé snímky ** Jsou na nich vidět artefakty všech misí programu Apolla, které přistály na povrchu Měsíce ** Jde například o části lunárních modulů, rovery a dokonce i vlajky

Petr Kubala | 67

Tajuplná čínská raketa by mohla ohrozit vzdušnou sílu amerického letectva

Tajuplná čínská raketa by mohla ohrozit vzdušnou sílu amerického letectva

** Čínské letectvo vyvíjí záhadnou raketu typu vzduch-vzduch ** Mysteriózní zbraň nese označení PL-XX ** Jde o raketu vzduch-vzduch s velmi dalekým doletem

Stanislav Mihulka | 14



Aktuální číslo časopisu Computer

Nejlepší programy pro úpravu fotek zdarma

Externí disky pro zálohu dat

Velký test: herní notebooky

Srovnání 12 batohů