Programowanie w języku Delphi » Artykuły

Zapisywanie ustawień w Delphi

  • 2012-01-28 17:15
  • 11 komentarzy
  • 2185 odsłon
  • Oceń ten tekst jako pierwszy
Spis treści

     1 Zapisywanie ustawień w Delphi
          1.1 Co zapisujemy?
     2 Rejestr systemu Windows
     3 Pliki INI
     4 Zapisywanie linijka po linijce
          4.1 Zapis:
          4.2 Odczyt:
     5 Pliki XML
          5.1 INI:
          5.2 XML:
          5.3 Komponenty
          5.4 Wiecej na temat XMLa
     6 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:


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:


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

Pliki XML


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 ?

INI:


[Sekcja]
Val=Wartość

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/I[...]s/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ł: http://4programmers.net/Delphi/Artykuły/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

kuty 2013-02-04 08:44

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.

p0358 2012-01-29 10:31

To z plikami INI się przydało :)

Patryk27 2011-01-19 10:51

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

jakubkrol 2009-09-19 09:13

@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

migajek 2009-09-19 00:21

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ł ;)

jakubkrol 2009-09-05 21:38

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

jakubkrol 2009-08-31 21:22

@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 ;]

MartinEagle 2009-08-31 18:32

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.

jakubkrol 2009-08-28 13:11

@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 ;]

gumamp 2009-08-28 12:27

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

Drajwer 2009-08-24 20:00

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