Umíme to s Delphi: 157. díl – řetězcové datové proudy

Dnešní článek vám podrobně a od základu vysvětlí, co jsou to řetězcové datové proudy reprezentované třídou TStringStream. Řekneme si nejen, co takové proudy mohou uchovávat, ale také k čemu mohou být dobré a jak je v praxi používat. Ukázková aplikace, která demonstruje praktické použití řetězových proud, nebude samozřejmě chybět.

Pojďme se podívat na další ze série článků věnovaných jednomu z poměrně elegantních způsobů nakládání s daty – s datovými proudy (streams). Dnešní článek je věnován dalšímu druhu proudů, avšak ještě předtím, než se pustíme do jeho samotné náplně, zopakujeme stručně, o čem jsme se bavili dosud.

Prozatím jsme se dozvěděli, že stream funguje jako jakési vodovodní potrubí: na jedné straně do něho lijeme vodu, na straně druhé jej kamsi připojíme. V důsledku toho voda jednoduše protéká z nádrže do jejího cílového umístění. Podobně funguje stream: na jedné straně jej kamsi připojíme (např. souborový stream do diskového souboru), na druhé straně do něho lijeme data. Po úvodním nakonfigurování streamu („připojení trubky“) data jednoduše protékají tak, jak chceme.

V diskusi pod jedním z předchozích článků zazněla zajímavá otázka: „proč mám třeba složitě používat streamy pro ukládání dat do souboru, když to, čeho pomocí streamů dokážu, mohu stejně dobře a mnohem elegantněji dosáhnout třeba použitím standardních metod komponent VCL, například SaveToFile. Jiný diskutér podal stručnou, ale velmi výstižnou odpověď: streamy použijeme proto, že s jejich pomocí je velmi jednoduché přejít z jednoho „cílového umístění“ na jiné. Pokud se například jednoho krásného dne rozhodneme, že data budeme chtít lít namísto do diskového souboru do síťového socketu, bude změna při použití streamů velmi, velmi jednoduchá. Právě v tomhle spočívá největší výhoda streamů – není obtížné rozhodnout se pro změnu používaného streamu a namísto diskového použít třeba paměťový a nebo síťový.

V předminulém dílu jsme společně vytvořili první jednoduchou aplikaci, na jejíž tvorbě jsme si demonstrovali, jakým způsobem zapisovat do streamu textové řetězce (což je v zásadě jedna z obtížnějších variant). Před týdnem jsme pokračovali: tuto aplikaci jsme přepracovali tak, že namísto do diskového souboru zapisovala údaje pouze do operační paměti. Jinak řečeno, namísto diskového streamu (TFileStream) jsme použili paměťový stream (TMemoryStream).

Pod minulým článkem se objevil příspěvek, jemuž je nutné dát za pravdu. Příspěvek říkal cosi ve smyslu „proč používáte takové neelegantní konstrukce, proč čtete z paměti tímhle a tímhle způsobem, když by to lo udělat mnohem jednodušeji.“ Ano, nehádám se: důvod, proč bylo v článku uvedeno právě tohle řešení, spočívá právě v tom, že jsem chtěl demonstrovat jednoduchost přechodu ze souborového streamu (který byl použit o jeden díl dříve) na paměťový stream. Aniž bychom aplikaci jakkoliv výrazně modifikovali, přešli jsme ze čtení souboru na čtení operační paměti. Právě to bylo cílem článku; bohužel to s sebou neslo jistou neeleganci, která vznikla převodem z předchozí verze aplikace.

Další druh streamu: řetězcový stream

Pojďme se však zaměřit na samotnou náplň dnešního článku. V něm si totiž ukážeme další druh streamu, který máme k dispozici. Nejedná se zrovna o stream, který bychom používali dnes a denně, ale o jeho existenci se minimálně vyplatí vědět, protože v některých případech se nám rozhodně může hodit. Hovoříme o tzv. řetězcovém streamu reprezentovaném třídou TStringStream.

TStringStream slouží (jak už jeho název napovídá) k uchovávání textových řetězců. Jinak řečeno, pokud by se nám z nějakého důvodu hodilo reprezentovat kterýkoliv řetězec. který v aplikaci používáme, jako stream, pak je třída TStringStream dobrou volbou. Pokud se vám ve vašich zvídavých programátorských myslích objevuje otázka typu „a kdypak by se nám tak mohlo hodit reprezentovat řetězec jako stream?“, potom se přímo nabízí odpověď typu „no přece kdykoliv, když budete programovat aplikaci používající streamy a budete si chtít ušetřit práci.“

Možná to teď není úplně jasné, ale pořád se bavíme o jedné a téže výhodě, kterou streamy přinášejí: o unifikované práci se všemi jejich druhy. Pokud budeme mít aplikaci, která odkudsi načítá, zobrazuje či zpracovává a dále předává stream, pak se může v určitých situacích hodit reprezentovat nějakou specifickou řetězcovou proměnnou jako proud: tím způsobem si zajistíme, že s ní můžeme pracovat stejně jako se všemi dalšími druhy streamů, které v aplikaci máme.

Sama firma Borland v nápovědě k Delphi uvádí: „Třídu TStringStream použijte v případech, kdy potřebujete uložit data jakožto long string a zároveň nad nimi chcete provádět nějaké vstupně-výstupní operace. TStringStream je užitečná jako meziobjekt, který dokáže udržovat data a kromě toho je číst nebo zapisovat z jiných záznamových médií.“

Třída TStringStream nemá zrovna moc metod a vlastností, podívejme se na následující seznam:

  • vlastnost DataString – poskytuje přímý přístup k řetězci, který objekt třídy zrovna uchovává. Jedná se read-only vlastnost.
  • vlastnost Size – uchovává informaci o délce aktuálního řetězce
  • vlastnost Position – uchovává informaci o aktuální pozici, na níž se ve streamu nacházíme
  • metody Read a ReadString – slouží k přečtení řetězce uchovaného ve streamu
  • metody Write a WriteString – slouží k zápisu řetězce do streamu

Toť v podstatě vše. Třída disponuje řadou dalších metod, ty jsou však odděděny od předků třídy a nevztahují se tedy specificky přímo k TStringStream.

TStringStream – praktická ukázka

Po pravdě řečeno, po dlouhé úvaze jsem stejně nepřišel na žádný smysluplný příklad použití třídy TStringStream, který by splňoval základní požadavky na příklady v tomto seriálu (jednoduchost a krátkost, názornost, využití co nejvíce metod/vlastností apod.). Proto omluvte smysl následujícího příkladu – po pravdě řečeno, následující příklad skoro žádný smysl ani nemá. Nicméně má jednu pozitivní vlastnost: demonstruje použití třídy TStringStream.

Aplikace, kterou společně vytvoříme, bude provádět následující činnost:

  • uživatel bude zadávat text do komponenty Memo
  • po klepnutí na tlačítko Ulož dojde k uložení obsahu komponenty Memo, a to do řetězcového streamu. Jinak řečeno, po klepnutí na tlačítko uložíme obsah Mema kamsi do operační paměti, nicméně uložená data budou reprezentována jako stream.
  • No a abychom si alespoň trochu naznačili, k čemu to může být dobré :-), tak po klepnutí na tlačítko Načti dojde k vložení uchovaných dat do komponenty RichEdit. Jinak řečeno, komponenta RichEdit si načte data z řetězcového streamu. Použijeme k tomu metodu LoadFromStream. Později si o standardních metodách vizuálních komponent pro práci s proudy povíme víc.

Neříkám, že takhle koncipovaná aplikace může někomu prakticky v čemkoliv pomoci :-) Jen chci, abyste viděli, jak jednoduché může být reálné (i když nepříliš využitelné) použití řetězového streamu.

Pojďme tedy vytvořit aplikaci. Vložme na formulář komponentu Memo ze záložky Standard palety komponent, dále komponentu RichEdit ze záložky Win32 palety komponent a v neposlední řadě dvě komponenty Button ze záložky Standard.

První, co uděláme, bude nadefinování privátního atributu Ret hlavního formuláře. Atribut Ret bude typu TStringStream a jeho definici vložíme přímo do definice třídy TForm1 v úvodní části hlavního modulu:

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    RichEdit1: TRichEdit;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
    Ret: TStringStream;

  public
    { Public declarations }
  end;

Nyní ošetříme událost OnCreate hlavního formuláře. V její obsluze provedeme kromě triviálního nastavení titulků komponent už jen jednu věc – vytvoříme samotný objekt Ret, tedy samotný proud. Parametrem konstruktoru Create je řetězec, který má být do proudu při jeho vytvoření ihned vložen. V našem případě chceme vytvořit prázdný proud:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Ret := TStringStream.Create(``);

  Button1.Caption := `Uloz`;
  Button2.Caption := `Nacti`;
  Self.Caption := `Ukazka TStringStream`;
end;

Nyní musíme pouze ošetřit klepnutí na obě tlačítka. Nejprve tlačítko Button1 sloužící k uložení Mema do proudu:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Ret.Position := 0;
  Ret.WriteString(Memo1.Lines.Text);
  Ret.Position := 0;
end;

Obsluha je velmi primitivní: nejprve se pro jistotu nastavíme na začátek proudu (Position := 0), abychom vždy zapisovali od začátku. Poté zapíšeme do proudu požadovaný řetězec (obsah komponenty Memo) za použití metody WriteString. Nakonec se opět nastavíme na začátek proudu, více méně pro jistotu, abychom při případném budoucím čtení z proudu četli zaručeně od prvního znaku.

Toť skoro vše. Posledním úkolem je ošetření klepnutí na Button2, které způsobí naplnění komponenty RichEdit aktuálním obsahem našeho oblíbeného řetězcového proudu:

procedure TForm1.Button2Click(Sender: TObject);
begin
  RichEdit1.Lines.LoadFromStream(Ret);
  Ret.Position := 0;
end;

Jak jsme si řekli výše, k nalití dat do RichEditu použijeme metodu LoadFroStream. Brzy si ukážeme, že řada vizuálních komponent disponuje obdobnými metodami pro načtení nějakého obsahu ze streamu nebo naopak pro uložení nějakého obsahu do proudu. Použití těchto metod je nesmírně jednoduché; krásně přitom demonstrují hlavní sílu streamů.

Podívejte se například na onen primitivní příklad, který jsme s velkou slávou vyrobili dnes. Předpokládejme, že poslední naprogramovaná metoda, tedy metoda pro načtení textu do RichEditu, není tak primitivní, jak je. Dejme tomu, že namísto triviálního naplnění RichEditu provádí s daty nějaké šílené a náročné transformace, ať už jde o nějaké prohledávání řetězce, nebo o jakékoliv analytické operace či cokoliv jiného. Všimněte si jedné věci: metoda by mohla zůstat implementována stejným způsobem, ať už by byl zdroj našich dat jakýkoliv.

Jinak řečeno, bez ohledu na to, jestli načítáme data z disku, z paměti, z řetězce, ze socketu, z databáze či odkudkoliv jinud, pokud použijeme pro práci s daty proudy, můžeme se v jednou okamžiku rozhodnout pro změnu zdroje dat; přitom však můžeme ponechat všechny „datazpracující“ metody kompletně beze změn.

Podívejte se prosím znovu na krátkou procedurku uvedenou výše. Kdybychom se najednou rozhodli, že do svého drahocenného RichEditu nebudeme načítat řetězec, ale třeba obsah diskového souboru, nemusíme do zdrojového kódu té metody ani sáhnout. Stačí někde „výše“ změnit deklaraci proměnné Ret (nebo spíš použít místo ní nějakého předka, například TStream) a někde jinde naplnit proměnnou Ret jiným druhem streamu, například souborovým nebo síťovým. To je všechno, všechny další metody, které data ze streamu čtou a dále zpracovávají, mohou zůstat naprosto beze změny. Právě tohle je v praxi jedna z největších výhod streamů. Jeden za všechny, všichni za jednoho.

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

Zdrojový kód

Závěrem se pojďme jako vždy podívat na kompletní zdrojový kód celého příkladu:

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    RichEdit1: TRichEdit;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
    Ret: TStringStream;

  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Ret.Position := 0;
  Ret.WriteString(Memo1.Lines.Text);
  Ret.Position := 0;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Ret := TStringStream.Create(``);

  Button1.Caption := `Uloz`;
  Button2.Caption := `Nacti`;
  Self.Caption := `Ukazka TStringStream`;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  RichEdit1.Lines.LoadFromStream(Ret);
  Ret.Position := 0;
end;

end.

Na závěr

Dnes jsme se podívali na další druh streamů – na řetězcové streamy reprezentované třídou TStringStream. Ukázali jsme si primitivní, avšak doufejme názorný příklad toho, jak mohou řetězcové streamy být použity v jednoduché reálné aplikaci. Zároveň jsme si zdůraznili hlavní výhodu streamů: mohou být jednoduše nahrazeny jeden druhým, aniž bychom museli předělávat klíčové, datazpracující části aplikace.

Diskuze (1) Další článek: Majitel WinZipu chystá zrušení trial verzí

Témata článku: Software, Windows, Programování, Textový řetězec, Private, Jednoduchý typ, Triviální nastavení, Elegantní způsob, Dobrá obsluha, Pozitivní vlastnost, Reálné použití, Hlavní modul, Lily, Řetěz, Uložená data, Proud, DEL, Následující aplikace, Díl, Stream, Lili


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

10 skrytých nastavení prohlížeče Google Chrome, která se můžou hodit

10 skrytých nastavení prohlížeče Google Chrome, která se můžou hodit

** Prohlížeč Google Chrome ukrývá mnoho zajímavých možností ** Našli jsme deset nejzajímavějších skrytých nastavení ** Můžete si například výrazně vylepšit práci s kartami

Karel Kilián | 18

Výkon herního počítače za 139 Kč nebo i úplně zadarmo. Geforce Now startuje!

Výkon herního počítače za 139 Kč nebo i úplně zadarmo. Geforce Now startuje!

** Dlouhý betatest končí a nyní všichni mohou hrát přes internet ** Nemusíte žádné hry kupovat znovu. Jede to, co už máte v knihovnách na Steamu, Uplay, Battle.net a jinde ** Roční náklady odpovídají ceně jedné běžné hry

Tomáš Holčík | 68

Windows 10X už si můžete vyzkoušet. Novému systému Microsoft zjevně věří

Windows 10X už si můžete vyzkoušet. Novému systému Microsoft zjevně věří

** Windows 10X přijdou již ke konci roku ** Microsoft vydal emulátor, kde systém ukázal ** Vývojáři musí upravit své aplikace

Vladislav Kluska | 58

Dnes nastal konec Windows 7. Ale nepropadejte panice, počítač vám nastartuje i zítra

Dnes nastal konec Windows 7. Ale nepropadejte panice, počítač vám nastartuje i zítra

** Dnes končí podpora Windows 7 a systém formálně umírá ** Co to ale znamená v praxi a bude mi PC fungovat i zítra? ** A mohu i v lednu 2020 zdarma přejít na Windows 10?

Jakub Čížek | 121



Aktuální číslo časopisu Computer

Megatest 12 bezdrátových sluchátek

Vyplatí se Apple z bazaru?

Test batohů pro notebooky

Vybíráme nejlepší sportovní hodinky