Zapisywanie ustawień w Delphi

jakubkrol

1 Zapisywanie ustawień w Delphi
     1.1 Co zapisujemy?
2 Rejestr systemu Windows
3 Pliki INI
4 Zapisywanie linijka po linijce
     4.2 Zapis:
     4.3 Odczyt:
     4.4 Komponenty
     4.5 Wiecej na temat XMLa
5 Dodatkowe funkcje i zakończenie

Zapisywanie ustawień w Delphi

Artykuł myślę przydatny każdej osobie. Od razu mowie, ze nie będzie to gotowiec, gdyż każdą z metod postaram się opisać, plusami i minusami, przykładami oraz podając kod i tłumacząc go.
Artykuł ten znajduje się także w oryginalne, podzielony tutaj: http://programowanie.66ghz.com/articles.php?article_id=10
A więc, zaczynamy!

Co zapisujemy?

Ustawienia, ale nie tylko. Czasami musimy po prostu, nawet tymczasowo przechować jakąś wartość, np. nazwę pliku, czy numer czegoś tam... Warto także w swoich programach udostępnić możliwość zapisywania ustawień w jakieś miejsce. Miejsce to jest zazwyczaj plikiem, a o możliwościach zapisu trochę niżej:

Rejestr systemu Windows

Metoda ciągle modna, szczególnie ze względu na szybkie jej działanie. Ja jednak tej metody nie polecam. Uważam ze jest dobra tylko, gdy z jakiejś wartości korzysta kilka osobnych aplikacji. Minusem tej metody jest bowiem ingerencja w rejestr i brzydko mówiąc czasami "zasyfianie" go. Niestety - niektórzy programiści nadużywają rejestru, który wcale nie jest tak łatwo oczyścić, w porównaniu do, np. systemu plików. Bardzo trudno będzie nam np. znaleźć wpis "HKCU/SOFTWARE/Microsoft/Windows/MojDodatek/Wpisy/KategoriaA: StaryWpis", gdy o nim po prostu nie wiemy. Dlatego starajmy się czyścic rejestr, jeżeli już go używamy.
Kolejnym, oprócz prędkości rejestru, jest prostota jego obsługi:

  1. Należy do listy modułów (uses) dodać słówko "Registry"
  2. Następnie deklarujemy zmienną typu "TRegistry"
  3. Należy ją utworzyć konstruktorem bez parametrów (Zmienna := TRegistry.Create;)
  4. Dalej (najlepiej w bloku try...finally) odczytujemy, czy zapisujemy rejestr używając różnorakich poleceń, o których nieco więcej dowiesz się później.
  5. Zwalniamy obiekt naszej zmiennej.
    Jeżeli chodzi o polecenia rejestru, to jest ich wiele: zapis stringów, liczb, dat, zmiennych typu boolean, pobieranie listy drzew (można zrobić własny edytor rejestru), wartości, tworzenie drzew itp.
    A oto przykładowy kod używania rejestru:
uses Registry(...)
(...)
procedure Foo;
var R: TRegistry; // Zmienna obiektu rejestru
begin
  R := TRegistry.Create; // Stworzenie zmiennej konstruktorem
  try
    R.RootKey:=HKEY_CURRENT_USER; // Wybranie głównej gałęzi
    R.OpenKey('Test\Galaz', True); // Stworzenie nowych gałęzi (poprzez otwarcie ich i przekazanie parametrem możliwości utworzenia, jeżeli nie istnieje)
    R.WriteString('UlubionaLiczba', '6669'); // Zapisanie do Rejestru zmiennej tekstowej
    X := R.ReadInteger('UlubionaLiczba'); // Odczytanie z Rejestru zmiennej liczbowej
  finally
    R.Free; // Zwolnienie naszego Obiektu w postaci zmiennej
  end;
end;

Jak widać, używanie rejestru jest dziecinnie proste.

Pliki INI

Polecana przeze mnie metoda, to używanie plików INI. Jednak znów zaśmiecają one system - tym razem folder Windows! Aby temu zapobiec, zawsze przed nazwa pliku INI stosujmy np. "ExtractFilePath(Application.ExeName)+'/...' ", choć krócej jest "ExtractFilePath(ParamStr(0))+'/...' " Pliki INI stosunkowo też łatwo się obsługuje, chodź nie tak bardzo jak rejestr. Plusami jest dosyć duża niezawodność, niezaśmiecanie systemu, przy odpowiednich parametrach oraz możliwość łatwego przenoszenia konfiguracji miedzy systemami. Plik INI jest także bardziej przejrzysty. Jego struktura może wyglądać, na przykład tak:

; Komentarz....
; Ustawienia programu Delete My Windows
[Settings]
WindowWidth=500
WindowHeight=355
[UserSettings]
UserName=Linux

Używanie go jest także bardzo proste i prawie identyczne, jak w wypadku rejestru Windows. Jego zdecydowaną wadą jest brak możliwości zapisu kilkulinijkowego tekstu do jednej wartości bezpośrednio. Aby to zrobić, musimy np. zapisywać linijka po linijce, czy zastępować Entery jakimiś znakami. Np:

[Info]
Notatki=linijka jeden linijka dwa
; Lub
[Info]
Notatki=3
[Notatki]
1= Pierwsza linijka
2= A tutaj druga
3= I tak dalej...

Ja osobiście tę metodę preferuje, szczególnie ze względu na łatwe modyfikacje, bez ingerowania w kod czy inne narzędzia (takie, jak np. Edytor Rejestru). A oto przykładowy kod do obsługi tychże plików:

uses INIFiles//(...)
//(...)
procedure Foo;
var Ini: TINIFile; // Tylko nie napisać tutaj  "Ini: TIniFiles" !
begin
  Ini := TINIFile.Create(ExtractFilePath(Application.ExeName)+'\Plik.Ini'); // Stworzenie zmiennej konstruktorem, którego jedynym parametrem jest podanie ścieżki pliku INI - oto cala trudność w porównaniu z rejestrem. Użyłem ExtractFilePath, aby plik pojawił się w folderze z programem. Domyślnie bowiem jest to folder Windowsa.
  try
    Ini.WriteString('INI', 'UlubionaLiczba', '6669'); // Zapisanie do pliku INI zmiennej tekstowej. Pierwszy parametr to nazwa sekcji, drugi nazwa wartości, a trzeci to wartość
    X := Ini.ReadInteger('INI', 'UlubionaLiczba', -1); // Odczytanie z pliku INI zmiennej liczbowej. Pierwszy parametr to nazwa sekcji, drugi nazwa wartości, a trzeci to wartość domyślna, jeżeli ta sekcja lub wartość nie istnieje
  finally
   Ini.Free; // Zwolnienie naszego Obiektu w postaci zmiennej
  end;
end;

Pliki INI to doskonały pomysł !

Zapisywanie linijka po linijce

Metody tej nie będę i nie chce mi się szczególnie długo omawiać. Powiem tylko, że jest to przestarzały i nieużywany (tj.bardzo rzadko używany) obecnie sposób zapisu ustawień, czy informacji. Dlaczego wiec o nim piszę ? Ponieważ jest przydatny przy zapisie po prostu, np. tekstu. W związku z tym nie będę go porównywał do plików INI, czy rejestru, gdyż obecnie służy do czegoś innego. Oczywiście można w nim zapisywać informacje, ale będą one bardzo mało przejrzyste i niewydajne.
A oto kod obsługi tego sposobu, linijka po linijce:

Zapis:

```delphi procedure Foo; var T: TextFile; // TextFile, a nie "File: String" ! begin Assign(T, 'C:\plik.txt'); // Nasz plik Rewrite(T); // Tryb nadpisywania Writeln(T, 'Lalala linia jeden!'); // Piszemy linijkę Writeln(T 'Ami6669 2009'); // Dopisujemy drugą Flush(T); // Tak dla pewności... Close(T); // I oczywiście, zamykamy plik end; ```

Odczyt:

```delphi procedure Foo; var T: TextFile; L: String; begin Assign(T, 'C:\plik.txt'); // Nasz plik Reset(t); // Otwieramy go, ale tym razem do odczytu

While (Not Eof(T)) Do // Dopóki nie jesteśmy na końcu pliku...
begin
ReadLn(T, L); // Odczytujemy tekst prosto do zmiennej L
Form1.Memo1.Lines.Add(l); // I tekst ze zmiennej L dodajemy do memo, które znajduje się na formie
end;

Close(t); // Zamykamy plik
end;


Jak widać, używanie tego nie jest zbyt praktyczne... 
Szczególnie, jeżeli chodzi o zapis danych i ustawień. Nie bądźmy jednak aż takimi sceptykami, co do tej metody - czasami będziemy skazani na takie wykonanie zadania, np. pisząc prosty notatnik, czy własną grę.

<h1>Pliki XML</h1>
Pliki XML działają podobnie do plików INI. Z tą różnicą, że sekcje są zamykane i otwierane - jak znaczniki HTML.
Jak wygląda porównanie ?
<h2>INI:</h2>

[Sekcja]
Val=Wartość

<h2>XML:</h2>
```xml
<sekcja>
  <podsekcja>
      <Val>Wartość</Val>
  </podsekcja>
</sekcja>

Pliki XML dają zatem o wiele większe pole do popisu - można zapisywać wielolinijkowy tekst, tworzyć własne sekcje, podsekcje i tak dalej... Nadawać wartościom parametry (z HTML'a). Mnie jednak osobiście XML nigdy nie przekonał, ze względu na swoją złożoność, która nie jest zawsze aż tak potrzebna. Jeżeli jednak interesuje Cię to, to zapraszam do poczytania więcej o plikach XML.

Komponenty

Pliki XML wymagają dodatkowych komponentów. Sam preferuję TJanXMLParser2. Komponenty te są proste w użytku i szybkie.

Polecam także EasyXML. Ta paczka zawiera ten komponent + demo aplikację + źródła + program do przeglądania, oskryptowywania itd, plików XML.

Pobrać je, wraz z przykładem można, np. stąd: http://www.softpedia.com/get/Internet/Servers/Database-Utils/EasyXML.shtml

Wiecej na temat XMLa

Nie będę się rozpisywał na temat XMLa, bo sam nie używam go tak często jak np. plików INI :)
Jeżeli jednak chcesz, to polecam artykuł: XML w Delphi Jest on bardzo obszerny i zawiera wiele informacji na temat używania XML'a w Delphi.

Dodatkowe funkcje i zakończenie

Warto pogłębiać się w wybrany przez siebie sposób zapisu danych. Dodam tutaj, że zapewne wybierzesz sobie jakiś ulubiony, lecz skazany będziesz na każdy z nich. W różnych przypadkach, różne sposoby zapisu ustawień, informacji, danych czy tekstu mogą się przydać. Wtedy zachęcam właśnie do zgłębiania funkcji i procedur konkretnych sposobów. Zobacz, że np. TRegistry może odczytać wszystkie dane klucze, a TINIFile potrafi zlistować do TStringList wszystkie sekcje pliku INI. To i o wiele więcej dowiesz się w zaciszu swojego środowiska Delphi zgłębiając tajniki zapisu informacji.

Powodzenia!

11 komentarzy

Jeżeli nie chce się komuś pisać zapraszam do gotowca na stronie http://programistakuty.pl/zapis-ustawien-formy/. Tam są zamieszczone dwa przykłady - na Ini i Registry, załączone dema.

To z plikami INI się przydało :)

Uff...Poprawiłem cały tekst. Teraz lepiej ?

@migajek:
Prawdę mówiąc, opisałeś tylko sposoby przechowywania ustawień w pliku ... szkoda że nie wspomniałeś o ich przechowywaniu w programie ;)
Otóż to - miałem za zadanie przechowywać po zamknięciu programu jakieś dane jego dotyczące :)

Bardzo fajnie, że masz taki pomysł na program - opisz to, jeżeli chcesz :)

Jedna drobna uwaga.. Zastanawiam się jaki to ma sens co mówisz? Po co obciążać pamięć i wszystko przechowywać? ustawienia się, aby coś sprawdzić, czy wprowadzić. Przechowywaniem ich zatem w programie nie ma sensu.. Bo przecież mozna użyć tego, np tak:
nick:=ini.ReadString('sekcja', 'nick', '');
czy
if ini.ReadBool('sekcja', 'autorun', False) then ...

:)

Pozdrawiam serdecznie

Prawdę mówiąc, opisałeś tylko sposoby przechowywania ustawień w pliku ... szkoda że nie wspomniałeś o ich przechowywaniu w programie ;)
Rekord, klasa? Lista "nazwa=wartosc", i wywoływanie funkcji szukającej wartości dla danej nazwy?
Tak się przypadkiem składa że akurat skończyłem implementację własnego systemu konfiguracji w Delphi ;) chyba popełnię o tym artykuł ;)

@wiatrak_ - dzięki wielkie za poprawki! :)

@MartinEagle:
Bardzo miło mi to słyszeć :)

I masz rację twierdząc, że stworzenie bazy danych w INI, czy (łatwiej) w XML-u jest możliwe :) Powiem więcej - miała by standardowy rozmiar i byłoby dosyć szybka!
Tak więc pisanie własnego systemu bazodanowego to fajny pomysł, interesujący, a co najważniejsze - do wykonania ;]

Przejrzałem ten artykuł z ciekawością, bo dobrze mieć takie wash&go, czyli 2 w 1... Znaczy się zestawienie sposobów na coś. Tak sobie myślę, że te metody zapisu ustawień można wykorzystać do stworzenia bazy danych (nie uwzględniając raczej pliku rejestru). Sporo zajmowałyby miejsca na dysku (INI, XML), ale dla nie wielkiej ilości rekordów można je zastosować, zamiast rozbudowywać kod o obsługę bazy danych.

@gumamp - masz rację, że do XML-a należy przyszłość.. Ale jakoś po prostu nie jest moim ulubionym sposobem zapisu informacji.. To samo można zapisywać w innych plikach, ale troche wiecej trzeba się napracować...
Nie pisałem o XMLu dużo, bo mało z nim pracuję, ale już robie edita i pisze o komponentach do jego użytku itd ;]

Ja xml to też bym mógł tak napisać. Ważne są szczegóły. Jakiego komponentu użyć do obsługi xml? Jak się nim posługiwać? To co napisałeś nic mi nie mówi. Gdybym tworzył programy które tylko zapamiętują rozmiar i pozycję okna aplikacji też bym się do xml'a nie przekonał. On jest przyszłością.

Słyszałeś o czymś takim jak formatowanie kodu ?!?!?! Popraw bo kaleczysz oczy.