Umíme to s Delphi: 159. díl – aktuální pozice ve streamu, velikost streamu

Dnešní článek se opět zabývá paměťovými proudy neboli streamy. Podrobně a prakticky si ukážeme nástroje Delphi pro kontrolu dvou věcí: aktuální pozice ve streamu a velikosti streamu. V ukázkové aplikaci se naučíme pracovat s uvedenými koncepty prakticky.

Vítejte u dalšího pokračování seriálu o programování v Delphi. V současnosti je naším hlavním tématemproblematika streamů neboli datových proudů. Ani dnešní článek nebude v tomto směru vyjímečný: podíváme se na některé dalí otázky, které mohou v souvislosti s datovými proudy vyvstat.

Ze všeho nejdříve je však nutné stručně shrnout, kam jsme došli a co jsme si pověděli v minulých částech seriálu. Dosud už víme, že stream funguje jako jakési potrubí: na jedné straně do něho cosi naléváme, na straně druhé jej kamsi připojíme. V důsledku toho tekutina 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.

Jednou z hlavních výhod streamu, a zároveň jedním z hlavních důvodů, proč streamy vlastně používat, je skutečnost, že je lze velmi jednoduše zaměňovat jeden za druhý. 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ředchozích částech seriálu jsme se podrobněji zabývali několika konkrétními druhy streamů a ukázali jsem si jejich praktické využití na demonstračních aplikacích. Jednalo se o následující streamy:

  • v dílu 157 – řetězcové datové proudy reprezentované třídou TStringStream
  • v dílu 156 – paměťové datové proudy reprezentované třídou TMemoryStream
  • v dílu 155 – souborové datové proudy reprezentované třídou TFileStream

V dílu 154 jsme se pak zabývali úplnými streamovými fundamenty a podali jsme si základní informace o tom, co streamy jsou a jak se používají. Díl předchozí, tedy díl s označením 158, se zabýval dvěma důležitými otázkami souvisejícími se streamy: za prvé, jak jsou streamy podporovány komponentami knihovny VCL (zjistili jsme, že značně, především díky metodám LoadFromSTream a SaveToStream, jejichž použití je stejně jednoduchoučké jako ranní nákup rohlíků) a za druhé, jak kopírovat jeden stream do druhého. Ani to není těžké, máme k dispozici metodu CopyFrom, která umožňuje stream naplnit daty z jiného streamu.

Toť ke stručnému shrnutí a zopakování minulých článků. Pojďme však už rychle k dnešní náplni – k otázce aktuální pozice v otevřeném streamu.

Pozice ve streamu

Z toho, co jsme si řekli v předchozích dílech seriálu a z toho, co jste viděli při vytváření některých ukázkových aplikací, zřejmě nikomu neunikla jedna zajímavá a důležitá věc: v každém streamu je spravována tzv. aktuální pozice. Neznamená to nic jiného než že při čtení ze streamu nebo při zápisu do streamu spravuje počítač automaticky aktuální čtecí nebo zápisovou pozici. Při standardním chování tak čteme stream obvykle od začátkua zapisujeme na jeho konec, správa aktuální pozice nám však dovoluje být mnohem originálnější a umožňuje nám zvolit si jiné místo, ze kterého chceme číst nebo do něhož chceme zapisovat.

O důležitosti institutu „aktuální pozice ve streamu“ asi nejlépe vypovídá následující informace: obecný předek všech streamů, třída TStream, obsahuje pouze dvě vlastnosti, ani jednu navíc. Jedná se o vlastnosti Size a potom právě Position. Pozice ve streamu je tedy očividně považována samotnými tvůrci Delphi za důležitou věc.

Pojďme se na problematiku aktuální pozice a jejího nastavení podívat podrobněji. Jak jsme si už řekli, kromě operací typu „čtení ze streamu“ nebo „zápis do streamu“ umožňují streamy aplikacím přejít na konkrétně zadanou pozici ve streamu nebo dokonce změnit velikost streamu. Jakmile nařídíme přejít na zadanou konkrétní pozici ve streamu, zajistíme, že následující operace čtení nebo zápisu započne provádět čtení nebo zápis právě na oné zvolené pozici.

Nejdříve se podívejme, jakým způsobem lze přejít na pozici, která nás z nějakého důvodu zajímá. Delphi poskytují v tomto směru jednu důležitou metodu: metodu Seek. Tato metoda je používána právě jakožto obecný mechanismus k posunu na konkrétní pozici v datovém proudu. Existují dvě přetížené verze metody Seek:

function Seek(Offset: Longint; Origin: Word): Longint;
function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;

Obě verze pracují v zásadě obdobným způsobem. Rozdíl spočívá pouze v tom, že první uvedená verze používá k reprezentaci pozice 32bitový integer , zatímco verze druhá používá k témuž účelu 64bitové číslo.

Co je však důležité: parametr Origin indikuje, jak má být interpretován parametr Offset. Podívejme se na to podrobněji: parametr Origin může nabývat následujících hodnot:

  • soFromBeginning – offset je brán od začátku streamu. Jinak řečeno, znamená to, že pozice ve streamu bude v zásadě nastavena na hodnotu parametru Offset pd začátku streamu. Offset musí být nezáporné číslo.
  • soFromCurrent – posuv ve streamu bude proveden o hodnotu Offset od aktuální pozice ve streamu. Takže, jsme-li ve streamu zrovna na teoretickém, imaginárním umístění 5 a zadáme-li Offset roven např. -2, dojde k posunu na pozici 3. Dochází tak vlastně k nastavení aktuální pozice na hodnotu Position + Offset, přičemž (jak z příkladu plyne) offset může být záporný.
  • soFromEnd – posuv je prováděn o hodnotu Offset od konce streamu. Hodnota Offset musí být v tomto případě nekladná (menší nebo rovna nule). Hovoříme-li například o souborovém streamu, zadáním soFromEnd a nastavením Offset na 20 způsobíme přechod na pozici (konec souboru minus 20 bajtů).

Použití metody Seek způsobí nastavení nové pozice ve streamu a tedy nastavením vlastnosti Poition na aktuálně nastavenou hodnotu. Seek kromě toho tuto aktuální hodnotu také vrací.

Důležitá poznámka: pokud pracujete se streamy a ze záhadného důvodu se vám nedaří Delphi přinutit, aby aplikace dělala to, co má a korektně četla / zapisovala do /ze streamů, doporučuji zkontrolovat právě oblast pozic ve streamu. Někdy bychom si přáli, aby streamy něco dělaly nebo nedělaly a jediným důvodem, proč streamy dělají / nedělají něco jiného, je právě nesprávně (nebo vůbec) kontrolovaná aktuální pozice.

Používání vlastností Position a Size

Jak jsme už také naznačili, všechny streamy (díky tomu, že zmíněné vlastnosti jsou k dispozici v jejich předku, ve třídě TStream) disponují vlastnosti pro uchovávání aktuální pozice a velikosti streamu. Tyto vlastnosti jsou používány metodou Seek, ale také všemi metodami, které mají za úkol číst ze streamu nebo do něho naopak zapisovat.

Vlastnosti se jmenují Position a Size. Vlastnost Position indikuje aktuální offset, v bajtech, ve streamu. Jinak řečeno, udává počet bajtů od začátku streamovaných dat. Deklarace vlastnosti Position říká, že jde o 64bitový integer.

Vlastnost Size indikuje velikost streamu v bajtech. Může být použita například k určení počtu bajtů dostupných pro čtení, nebo k oříznutí dat ve streamu. Jedná se také o 64bitový integer. Vlastnost Size je zhusta používána interně rutinami čtoucími a zapisujícími data ze / do streamů.

Nastavením vlastnosti Size změníme velikost dat ve streamu. Podívejme se na příklad: pokud máme souborový stream a nastavíme jeho velikost prostřednictvím vlastnoasti Size, způsobíme vložení značky konce souboru na zadanou pozici. V důsledku může třeba dojít k oříznutí souboru.

Pokud nemůže být vlastnost Size změněna (například při pokusu změnit velikost souboru určenému pouze pro čtení apod.) a my se o nastavení její hodnoty přesto pokusíme, dojde k vygenerování výjimky.

Je samozřejmé, že v některých případech nedává „změna velikosti streamu“ žádný smysl, proto v některých typech streamů nezpůsobí nastavení vlastnosti Size vůbec nic. Použití Size má smysl pouze u některých streamů: v takových případech jsou na to příslušní potomci třídy TStream připraveni a příslušné operace implementují.

Ukázková aplikace

Pojďme nyní společně vytvořit velmi jednoduchou aplikaci, která demonstruje použití vlastností Position a Size.

V Delphi vytvořte novou aplikaci, na formulář vložte 3 komponenty Label ze záložky Standard, komponentu Edit ze záložky Standard a komponentu TrackBar ze záložky Win32.

Co bude naše aplikace dělat? Nic moc :-) Po spuštění se otevře soubor zadaného jména (jméno zadáváme pro jednoduchost přímo ve zdrojovém kódu, bylo by jistě elegantnější nechat uživatele soubor zvolit za běhu) a prvních deset znaků tohoto souboru se vypíše do komponenty Edit. Následně posuvný jezdec komponenty TrackBar je svázán s pozicí v onom souboru. Při jakémkoliv posunu jezdce v komponentě TrackBar dojde ke změně pozice v souboru a k vypsání deseti znaků od této nové aktuální pozice do komponenty Edit.

Asi nikoho nepřekvapí, že použijeme streamy. Soubor tedy svážeme se souborovým streamem a při posuvu jezdce v TrackBaru jednoduše nastavíme novou pozici ve streamu. Z této nové pozice pak přečteme 10 bajtů a vypíšeme do Memo.

Ve zdrojovém kódu vypíchněme několik bodů. Nejprve nadefinujeme dva privátní atributy třídy TForm: souborový stream a ukazatel na znak. Tyto atributy budeme používat v několika dalších metodách:

type
  TForm1 = class(TForm)
    TrackBar1: TTrackBar;
    Label1: TLabel;
    Label2: TLabel;
    Edit1: TEdit;
    Label3: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure TrackBar1Change(Sender: TObject);
  private
    { Private declarations }
    x : TFileStream;
    buf : Pchar;
  public
    { Public declarations }
  end;

Následně v proceduře pro vytváření formuláře nastavíme titulky komponent, nastavíme rozsah jezdce v TrackBaru, nastavíme frekvenci čárek pod TrackBarem, nastavíme barvu a read-only komponenty Edit, a přečteme ze začátku streamu prvních deset znaků. Všimněte si, že pro zjištění rozsahu TrackBaru použijeme právě vlastnost Size našeho streamu:

procedure TForm1.FormCreate(Sender: TObject);
begin
   x := TFileStream.Create(`c:\sedinst2.log`, fmOpenRead);
   TrackBar1.Max := x.Size;
   TrackBar1.Frequency := 20;
   Label1.Caption := `0`;
   Label2.Caption := IntToStr(TrackBar1.Max);
   Label3.Caption := `0`;
   Caption := `Ukazka Position a Size`;

   Edit1.ReadOnly := True;
   Edit1.Color := RGB(250, 180, 190);

   GetMem(buf, 10);
   x.Read(buf^, 10);
   Edit1.text := buf;
end;

V proceduře pro uzavření formuláře uvolníme paměť i stream:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  x.free;
  FreeMem(buf);
end;

Poslední událost, která nás zajímá, je OnChange komponenty TrackBar. Při změně pozice jezdce přepočítáme a nastavíme aktuální pozici ve streamu a načteme z této pozice deset znaků. Ty zobrazíme v Editu.

procedure TForm1.TrackBar1Change(Sender: TObject);
begin
   x.Position := trackbar1.Position;
   x.Read(buf^, 10);
   Edit1.text := buf;
   Label3.Caption := inttostr(trackbar1.Position);
end;

Toť vše, aplikace je jednoduchá až primitivní, ale snad alespoň demonstruje, jak funguje nastavování vlastnosti ve streamu.

Pozor: nijak podrobně neošetřujeme skutečnost, že se blížíme ke konci streamu a že tedy teoreticky nemáme co číst. Korektní by jistě bylo na tuto skutečnost ve zdrojovém kódu pamatovat.

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

Zdrojový kód

Podívejme se na kompletní zdrojový kód příkladu:

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    TrackBar1: TTrackBar;
    Label1: TLabel;
    Label2: TLabel;
    Edit1: TEdit;
    Label3: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure TrackBar1Change(Sender: TObject);
  private
    { Private declarations }
    x : TFileStream;
    buf : Pchar;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
   x := TFileStream.Create(`c:\sedinst2.log`, fmOpenRead);
   TrackBar1.Max := x.Size;
   TrackBar1.Frequency := 20;
   Label1.Caption := `0`;
   Label2.Caption := IntToStr(TrackBar1.Max);
   Label3.Caption := `0`;
   Caption := `Ukazka Position a Size`;

   Edit1.ReadOnly := True;
   Edit1.Color := RGB(250, 180, 190);

   GetMem(buf, 10);
   x.Read(buf^, 10);
   Edit1.text := buf;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  x.free;
  FreeMem(buf);
end;

procedure TForm1.TrackBar1Change(Sender: TObject);
begin
   x.Position := trackbar1.Position;
   x.Read(buf^, 10);
   Edit1.text := buf;
   Label3.Caption := inttostr(trackbar1.Position);
end;

end.

Na závěr

Dnešní článek byl zaměřen na podrobný popis dvou konceptů: aktuální pozice ve streamu a velikosti streamu. Věřím, že jste se dozvěděli ve, co jste se o těchto dvou bodech chtěli a potřebovali dozvědět. Těším se na shledanou za týden.

Diskuze (1) Další článek: Firefox začíná ztrácet podíl?

Témata článku: Software, Programování, Pokračování seriálů, Private, Lili, Důležitá věc, Lily, Stream, Důležitý soubor, Tekutina, Důležitá poznámka, Druhá strana, Následující aplikace, DEL, Pozice, Díl, Origin, Ukázková aplikace, Rohlík.cz, Rohlík, Akt, Jednoduchá pozice, Proud, Ukázkový příklad, Str


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

Zapomeňte na kometu, české nebe každý den křižují mnohem zajímavější kousky

Zapomeňte na kometu, české nebe každý den křižují mnohem zajímavější kousky

** České nebe každý den křižuje hromada exotických letounů ** Na populární mapě Flightradar24 je ale nenajdete ** Jsou to vojenské letouny USA, UK a NATO

Jakub Čížek | 39

20 let nám vědci slibují revoluční baterie, ale revoluce se pořád nekoná

20 let nám vědci slibují revoluční baterie, ale revoluce se pořád nekoná

** Technologie baterií se stále zlepšuje, ale žádné revoluce se nekonají ** Nejpopulárnějším typem baterií je Li-ion ** Efektivní baterie se stávají důležitější s příchodem elektromobilů

Karel Javůrek | 96

Podívejte se, co dokáže vyrobit jedna z nejexotičtějších 3D tiskáren v Česku

Podívejte se, co dokáže vyrobit jedna z nejexotičtějších 3D tiskáren v Česku

** Na jaře tiskla unikátní české respirátory ** Používá ji třeba Škoda Auto, a.s. ** Zajeli jsme se podívat do pražského showroomu 3Dees

Jakub Čížek | 12


Aktuální číslo časopisu Computer

Megatest mobilů do 8 000 Kč

Test bezdrátových headsetů

Linux i pro začátečníky

Jak surfovat anonymně