Squeak: návrat do budoucnosti (15)

18. 5. 2004
Doba čtení: 6 minut

Sdílet

Proudové třídy jsou pro praxi velmi důležité. Dnes se na ně zaměříme. Ukážeme si použití jejich nejdůležitějších metod a popíšeme si základy práce se soubory.

Proudy jsou objekty, které slouží ke zpřístupňování sekvencí objektů. Jedná se o instance tříd, které dědí z abstraktní třídy Stream. Samozřejmě ne všechny operace mají smysl pro všechny typy proudů. Například do proudu určeného pouze pro čtení nelze zapisovat apod. Jsou vždy provázány s nějakým sekvenčním datovým zdrojem, v němž si udržují informace o aktuální pozici.

next, next:, peek

Zpráva next vrátí další prvek z proudu. V okamžiku, kdy již v proudu žádné objekty nejsou, je vrácena hodnota nil.

Obdobně metoda next: vrací kolekci více objektů z proudu. Jejich počet je definován v parametru této zprávy. Typ vrácené kolekce je závislý na třídě, nad kterou je proud vytvořen.

stream := 'text' readStream.
stream next -> $t
stream next -> $e
stream next: 2 -> 'xt'

stream := #(1 2 3) readStream.
stream next -> 1
(stream next: 2) class -> Array

stream := #(3 2 6) asSortedCollection readStream.
items := stream next: 2.
items contents -> a SortedCollection(2 3)
items class -> SortedCollection

Pro přečtení předcházejícího prvku slouží metoda back.

Proudy obsahují celou řadu metod, které specifikují, jakým způsobem mají být přijímaná data interpretována. Můžete tak nalézt zprávy jako nextInt32, nextWord, nextNumber:, nextLittleEndi­anNumber:, int16 apod. Tyto metody často vyžadují binární proudy.

Zpráva peek má podobný význam jako next, ovšem nedochází ke změně aktuální pozice v proudu.

stream := 'text' readStream.
stream peek -> $t
stream peek -> $t

contents, upToEnd

Zpráva contents vrací všechny prvky v proudu. Metoda upToEnd vrací všechny prvky od aktuální pozice do konce proudu.

stream := 'text' readStream.
stream next.
stream contents -> 'text'

stream := 'text' readStream.
stream next.
stream upToend -> 'ext'

Zpracování proudu

Ke zpracování celého obsahu proudu můžeme použít starou známou metodu do:. Pracuje stejně jako u kolekcí. Zpracováván je vždy obsah do konce proudu počínaje aktuální pozicí.

stream := 'text' readStream.
stream next.
stream do: [:znak |
   Transcript
      show: (znak asciiValue bitXor: 2r10101010);
      space.
   "vypise 207 210 222"
]

Pohyb v proudu

Pro zjištění aktuální pozice v proudu slouží zpráva position. Začátek proudu má index 0. Pozici lze explicitně změnit zprávou position:. Pokud nová pozice přesahuje rozsah proudu, je vygenerována výjimka.

stream := 'text' readStream.
stream position -> 0
stream position: 2.
stream next -> $x

O tom, zda se již nacházíme na konci proudu, nás informuje zpráva atEnd vracející boolovskou hodnotu.

elements := OrderedCollection new.
[ stream atEnd ] whileFalse: [
      elements add: stream next
].

Na začátek proudu nás přemístí metoda reset.

stream := 'text' readStream.
stream next: 2.-> 'te'
stream reset.
stream next -> $t

Na konec proudu nás naopak přesune metoda setToEnd. Pro přeskakování specifikovaného počtu prvků proudu slouží metodaskip:. Její obdoba skipTo: se posunuje v proudu až do chvíle, kdy v něm nalezne objekt shodný s tím, který obdrží jako parametr.

Vyhledávání a porovnávání v proudu

Pro vrácení obsahu proudu po určitý objekt slouží zpráva upTo:. Pokud objekt není v proudu nalezen, je vrácen celý zbytek proudu.

stream := 'text' readStream.
stream upTo: $x -> 'te'

Podobně funguje i zpráva upToAll:, která ovšem neporovnává jediný objekt, ale celou kolekci objektů

stream := #(6 5 1 8 6 7 2) readStream.
stream upToAll: #(8 6) -> #(6 5 1)

stream := 'Answer a subcollection' readStream.
stream upToAll: 'collection' -> 'Answer a sub'

Zprávy nextMatchFor: a nextMatchAll: porovnávají následující prvky v proudu a vracejí příslušnou boolovskou hodnotu. Pozici v proudu mění při kladném i záporném výsledku. Metoda, která při selhání porovnání prvku v proudu neposune pozici, se jmenuje peekFor:Pokud je porovnávána kolekce objektů pomocí nextMatchAll: a dojde k neshodě, pozice v proudu se nezmění.

stream := 'text' readStream.
stream nextMatchFor: $t -> true
stream nextMatchAll: 'ex' -> true
stream nextMatchFor: $a -> false

Ke zjištění, zda proud obsahuje danou kolekci prvků, slouží metodamatch:. Při nalezení shody kolekce s proudem je pozice v proudu nastavena na následující objekt.

Zápis do proudu

K zápisu musí být proud přizpůsoben, nejlépe tak, že je instancí třídy WriteStream. Ta naopak vůbec neumožňuje čtení z proudu.

K zápisu do proudu slouží primárně metody nextPut: a nextPutAll:. Druhá uvedená přijímá jako parametr kolekci či proud.

stream := WriteStream on: String new
stream nextPut: $a.
stream nextPutAll: 'bcd'.
stream contents -> 'abcd'

Tyto dvě základní jsou samozřejmě rozšířeny mnoha dalšími specializovanými zprávami, např. next:putAll: vkládající specifikovaný počet prvků z dané kolekce apod.

K současnému čtení a zápisu do proudu je přizpůsobena třída ReadWriteStream. Po vytvoření je připravena k zápisu na konci proudu.

stream := ReadWriteStream with: 'abcd'.
stream reset.
stream next.
stream nextPut: $e.
stream contents -> 'aecd'

Vytvoření proudu

Proudy se nejčastěji vytvářejí nad kolekcemi nebo soubory. Některé způsoby jejich tvorby jsme si již ukázali.

K vytváření proudů se nesmí používat metoda new. Vždy jsou svázány se zdrojem dat a ten je nutno na počátku jejich existence specifikovat. K tomu slouží konstruktory on:, with:,on:from:to: vytvářející proud jen na části vstupní kolekce atd.

Další možností je použít konverzní metody readStream a writeStream, které jsou implementovány ve třídě SequenceableCo­llection, takže jim rozumí pole, řetězce, seznamy apod.

Proudy nad soubory

Pro práci se soubory se využívá třída FileStream a její potomci. K asociaci proudu s fyzickým souborem existuje několik zpráv, jejichž význam je patrný už ze selektorů. Před třídou FileStream se doporučuje upřednostňovat třídu StandardFileStream. FileStream má totiž abstraktní charakter (pokud se využívá přímo, deleguje StandardFileS­tream).

fileNamed:
otevře soubor daného jména pro čtení a zápis. Pokud tento soubor neexistuje, je vytvořen nový soubor. Pozice (i pro zápis) je nastavena na začátek souboru.
forceNewFileNamed:
otevře nový soubor daného jména pro čtení a zápis. Pokud tento soubor již existuje, je původní soubor smazán.
newFileNamed:
otevře nový soubor pro čtení a zápis. Pokud tento soubor již existuje, je o tom podána uživateli zpráva, a ten má možnost soubor přepsat či vybrat jiné jméno.
oldFileNamed:
otevře existující soubor pro čtení a zápis. Pokud tento soubor ještě neexistuje, má uživatel možnost jej vytvořit nebo zvolit jiné jméno souboru.
oldFileOrNoneNamed:
otevře existující soubor pro čtení. Pokud tento soubor neexistuje, je vráceno nil.
readOnlyFileNamed:
otevře existující soubor pro čtení. Pokud tento soubor neexistuje, má uživatel možnost zvolit jiné jméno.

Další užitečné metody, které poskytuje třída FileStream:

isAFileNamed:
prověří existenci souboru daného jména
fullName:
vrátí plné jméno souboru včetně cesty.

Hodit se může metoda contentsOfEnti­reFile, která vrací celý obsah souboru a rovnou se postará i o jeho zavření.

Pro další práci se soubory, jako je např. kopírování, slouží třída FileDirectory a její platformně specifické podtřídy.

Zavírání souborů

Pro zavírání souborů se používá zpráva close. Pokud je reference na soubor zrušena, postará se finalizační mechanismus o jeho uzavření. Nicméně – především pokud si se soubory hrajete ve Workspace – se vám může stát, že na neuzavřený soubor bude stále odkazovat nějaká zapomenutá reference. V tom případě jej již pravděpodobně znovu neotevřete, nebudete schopni ho smazat apod.

U běžných programů, pokud nejsou ukončeny korektně, se o uvolnění prostředků stará operační systém. Pokud vinou chyby ve vaší aplikaci nezhavaruje celý Squeak, k uzavření otevřených souborů nedojde.

V takovém případě je nutné nasadit některou z technik pro vyhledávání a práci se zapomenutými referencemi. Nejjednodušší je použít např. příkaz

StandardFileStream inspectAllSubinstances

a v otevřeném inspektoru zapomenuté soubory vyhledat a zaslat jim zprávu close.

bitcoin školení listopad 24

Konce řádků

Squeak má své kořeny na počítačích společnosti Apple. Díky tomu nepoužívá standardně pro konce řádků znak LF ani dosovskou kombinaci CR LF, ale znak CR. V některých specifických situacích se můžete setkat s problémy, k jejichž řešení může pomoci např. třída CrLfFileStream.

Tímto jsme problematiku proudů ve Squeaku ještě zdaleka nevyčerpali. Neméně zajímavé jsou specializované proudy, komprimované proudy, sokety, image segmenty apod. O nich ale snad někdy příště.