Kategória: ObjectPascal

Zmenené: 8. december 2008

Dynamické zoznamy reťazcov

V programoch je často potrebné uchovávať zoznamy dát. Pascal pre túto úlohu ponúka typ pole, ktoré však má jednu veľkú nevýhodu, a to, že počet prvkov poľa musíte určiť už pri deklarácii poľa. Ponúka aj možnosť vytvárania dynamických polí, ale moderný objektový Pascal ponúka možností oveľa viac.

Free Pascal poskytuje prostriedky na vytváranie dynamických zoznamov pomocou ukazovateľov, ale kto to skúšal vie, že to nie je triviálny záležitosť a je potrebné ošetriť celú prácu so zoznamom. Inými slovami je to kopmlexná úloha. Free Pascal na to zavádza triedu, ktorá celý proces zapúzdruje.

Na spravovanie zoznamov reťazcov zavádza Free Pascal vo svojej behovej knižnici triedu TStringList, ktorá je definovaná v jednotke Classes. TStringList je potomkom abstraktnej triedy TStrings, preto najprv popíšem obe triedy. Potom sa pokúsim ukázať využitie TStringList pomocou príkladov.

V knižnici vizuálnych komponentov Lazara môžme triedu TStringList (respektíve jej predka TStrings) nájsť napríklad v komponentoch ako TMemo, TComboBox, či TListBox. Takže prácu s dynamickými zoznammi reťazcov pomocou triedy TStringList možno využiť aj pri používaní vizuálnych komponentov a nie len pri používaní programov príkazového riadku.

TStrings

TStrings implementuje abstraktnú triedu na spravovanie poľa reťazcov. Zavádza metódy na vkladanie a získavanie reťazcov v poli, hľadanie reťazcov, ich spájanie a podobne. Umožňuje tiež každému reťazcu priradiť dodatočný objekt. Zavádza aj metódy pre spravovanie nastavení v podobe dvojíc meno=hodnota, ktoré sú bežné v rôznych konfiguračných súboroch.

Inštancia triedy TStrings nemá byť nikdy priamo vytváraná, namiesto toho vytvárajte jej potomkov (napr. TStringList), pretože TStrings je abstraktná trieda, ktorá neimplementuje všetky metódy. TStrings tiež neuchováva žiadne reťazce, táto vlastnosť je funkčná až v potomkoch (TStringList).

Metódy triedy TStrings

  • Add – pridáva do zoznamu reťazec;
  • AddObject – pridáva do zoznamu reťazec a priradený objekt;
  • AddStrings – pridáva k tomuto zoznamu obsah iného zoznamu reťazcov;
  • Append – pridáva do zoznamu reťazec;
  • Assign – priraďuje k tomuto zoznamu obsah iného zoznamu reťazcov;
  • BeginUpdate – označuje začiatok aktualizačnej dávky;
  • Clear – odstraňuje zo zoznamu všetky reťazce a priradené objekty;
  • Delete – odstraňuje zo zoznamu reťazec;
  • Destroy – uvoľňuje všetky reťazce a objekty, a odstraňuje zoznam z pamäte;
  • EndUpdate – označuje koniec aktualizačnej dávky;
  • Equals – porovnáva obsah dvoch zoznamov reťazcov;
  • Exchange – vymieňa dva reťazce v zozname medzi sebou;
  • GetNameValue – vracia meno aj hodnotu, pár meno-hodnota podľa jeho indexu;
  • GetText – vracia obsah ako PChar;
  • IndexOf – nájde v zozname reťazec a vráti jeho pozíciu;
  • IndexOfName – hľadá index mena v páre meno-hodnota;
  • IndexOfObject – hľadá v zozname objekt a vracia jeho index;
  • Insert – vkladá do zoznamu reťazec;
  • InsertObject – vkladá do zoznamu reťazec a priradený objekt;
  • LoadFromFile – načíta obsah zoznamu zo súboru, ako postupnosť reťazcov;
  • LoadFromStream – načíta obsah zoznamu zo streamu, ako postupnosť reťazcov;
  • Move – presúva reťazec z jedného miesta zoznamu na iné;
  • SaveToFile – ukladá obsah zoznamu do súboru;
  • SaveToStream – ukladá obsah zoznamu do streamu;
  • SetText – nastavuje obsah zoznamu z PChar.

Vlastnosti triedy TStrings

  • Capacity (rw) – kapacita zoznamu, teda počet reťazcov, ktoré môže zoznam obsahovať pred jeho rozšírením;
  • CommaText (rw) – obsah zoznamu ako čiarkou oddelený reťazec;
  • Count (ro) – počet reťazcov v zozname;
  • DelimitedText (rw) – vracia alebo nastavuje všetky reťazce v zozname odedelené oddeľovačom;
  • Delimiter (rw) – znak oddeľovača použitý v DelimitedText;
  • Names (ro) – časť meno z páru meno-hodnota v zozname;
  • NameValueSeparator (rw) – hodnota znaku použitá na oddelenie na oddelenie mena a hodnoty v páre;
  • Objects (rw) – indexovaný prístup k objektom priradeným k reťazcom v zozname;
  • QuoteChar (rw) – znak úvodzovky použý v DelimitedText;
  • Strings (rw) – indexovaný prístup k reťazcom v zozname;
  • StringsAdapter (rw) – neimplementované vo Free Pascal.
  • Text (rw) – obsahuje zoznam ako jeden veľký reťazec;
  • ValueFromIndex (rw) – vracia časť hodnoty na základe jeho indexu;
  • Values (rw) – časť hodnota z páru meno-hodnota v zozname.

TStringList

TStringList je potomkom triedy TStrings, ktorá implementuje všetky abstraktné metódy zavedené v TStrings. Okrem toho zavádza aj nové metódy:

  • zoradenie zoznamu alebo udržiavanie zoznamu stále zoradeného
  • špeciálnu obsluhu duplikátov v zoradených zoznamoch
  • informovanie o zmenách v zozname

Metódy triedy TStringList

  • CustomSort – umožňuje priradiť vlastnú implementáciu algoritmu radenia;
  • Find – vyhľadáva index daného reťazca v zoradenom zozname;
  • Sort – triedi reťazce v zozname.

Vlastnosti triedy TStringList

  • CaseSensitive (rw) – udáva, či hľadanie reťazcov má byť citlivé na veľkosť;
  • Duplicates (rw) – popisuje správanie zoradeného zoznamu s ohľadom na duplikáty reťazcov;
  • Sorted (rw) – určuje, či je zoznam zoradený alebo nie.

Udalosti triedy TStringList

  • OnChange – udalosť vyvolaná po zmene zoznamu;
  • OnChanging – udalosť vyvolaná ke je zoznam menený.

Používanie TStringList

S triedou TStringList, alebo teda presnejšie s jej predkom TStrings, sa stretnete v komponentoch ako TMemo, TComboBox, či TListBox. Práve na komponente TMemo sa pokúsim ukázať ako využiť možnosti tejto triedy. Komponent TMemo má vlastnosť Lines, ktorá je typu TStrings. Áno TStrings, nie je to preklep, a to napriek tomu, že som skôr písal o TStrings ako o abstraktnej triede, ktorá nemá byť priamo používaná. Táto trieda je použitá ako predok tried, a tak je možné vlastnosti Lines priradiť akéhokoľvek potomka TStrings, čiže aj TStringList.

Pre našu ukážku si vytvorte nový projekt s jednym formulárom, do ktorého vložte komponenty podľa priloženého kódu.

Definícia formulára

type
    { TfrmSort }
    TfrmSort = class(TForm)
        btnSort: TButton;
        btnClose: TButton;
        memText: TMemo;
        private
        { private declarations }
        public
        { public declarations }
    end;

var
    frmSort: TfrmSort;

implementation

{ TfrmSort }
procedure TfrmSort.btnCloseClick(Sender: TObject);
begin
    Application.MainForm.Close;
end;

Ako môžete v kóde vidieť, formulár má meno frmSort a obsahuje dve tlačidlá (btnSort a btnClose), jedno textové pole (memText). Priradená je aj udalosť OnClick tlačidlo btnClose, ktorá má za úlohu zatvoriť aplikáciu.

Zoradenie položiek

Ako prvé si môžeme vyskúšať zoradenie položiek. Do udalosti OnClick, tlačidla btnSort priradíme obsluhu, ktorá sa o zoradenie postará:

Aplikácia triedenia zoznamu:

procedure TfrmSort.btnSortClick(Sender: TObject);
var
    lStrList: TStringList;
begin
    try
        lStrList := TStringList.Create;
        lStrList.Assign(memText.Lines);
        lStrList.Sort;
        memText.Lines := lStrList;
    finally
        FreeAndNil(lStrList);
    end;
end;

Vzhľadom na to, že vlastnosť Lines, komponenty memText je typu TString, a táto trieda nemá triedenie implementované, potrebujeme použiť lokálnu premennú typu TStringList. Najprv vytvoríme inštanciu triedy lStrList, do ktorej následne pomocou metódy Assign priradíme obsah vlastnosti Lines komponentu memText. Inštancia triedy TStringList už metódu Sort implementovanú má, takže môžeme zoznam zoradiť a potom priradiť naspäť do komponentu - v tomto prípade už jednoduchým priradením.

Za pozornosť stojí, že celá táto operácia je implementovaná pomocou ošetrenia výnimiek (chýb), teda bloku try - finally - end. Významom tejto konštrukcie je, že či už sa v bloku medzi try a finally vyskytne výnimka alebo nie, vždy je nakoniec vykonaný kód medzi finally a end, inými slovami aj keď sa vyskytne niečo neplánované, je objekt lStrList vždy uvoľnený.

Na obrázkoch môžete vidieť náhodne zadaný text a výsledok jeho zoradenia:

../../_images/frmSort01.png ../../_images/frmSort02.png

Pridanie položky

Pre príklad pridávania položky do TStringList rozšírime náš program o ďalšie ovládacie prvky, a to o jedno editačné pole edtText a tlačidlo btnAdd. Pridanie položky umožňuje metóda Add, ktorá je implementovaná aj v triede TStrings, takže pri práci s komponentom TMemo nemusíme používať pomocnú premennú, ale môžeme položku pridať priamo. Do obsluhy udalosti OnClick tlačidla btnAdd pridáme tento kód:

Pridanie záznamu:

procedure TfrmSort.btnAddClick(Sender: TObject);begin
    if edtText.Text <> '' then
    begin
        memText.Lines.Add(edtText.Text);
        edtText.Clear;
    end
    else
        ShowMessage('Textové pole je prázdne!!!');
    end;

V tejto obsluhe najprv testujeme, či editačné pole vôbec obsahuje nejaký text. Ak nie, program vypíše hlásenie, ak pole nie je prázdne, pridá metóda Add jeho obsah na koniec textu ako posledný riadok. Po pridaní textu je obsah editačného prvku zmazaný metódou Clear. Nasledujúce obrázky ukazujú stav aplikácie pred a po pridaní položky:

../../_images/frmSort03.png ../../_images/frmSort04.png

Keďže sme nemenili obsluhu udalosti tlačidla btnSort, stále ho môžete použiť na zoradenie obsahu TMemo.

Vloženie položky

Niekedy nie je postačujúce pridávanie položky na koniec zoznamu, ale je potrebné vložiť novú položku na začiatok, či iné vopred určené miesto. Na toto poslúži metóda Insert, ktorá má dva parametre. Prvý je celé číslo a udáva číslo riadku, na ktorý bude položka vložená (prvý riadok je nula), za ním nasleduje samotný text položky. Do formulára pridáme ďalšie tlačidlo btnIns a do obsluhy jeho udalosti OnClick tento kód:

Vloženie položky:

procedure TfrmSort.btnInsClick(Sender: TObject);
begin
    if edtText.Text <> '' then
    begin
        memText.Lines.Insert(0, edtText.Text);
        edtText.Clear;
    end
    else
        ShowMessage('Textové pole je prázdne!!!');
    end;

Princíp vloženia je rovnaký ako vpredchádzajúcom príklade, vrátane ošetrenia obsahu editačného poľa, či jeho vymazania po vložení. Výsledok potom môže vyzerať takto:

../../_images/frmSort05.png ../../_images/frmSort06.png

Na rozdiel od komponenta TListBox, TMemo neposkytuje jednoduchý spôsob ako zistiť, na ktorom riadku sa nachádza kurzor, takže pridanie položky na určitú pozíciu môže byť netriviálna úloha.