Datový proud
Datové proudy (anglicky stream; dále jen „proudy“) jsou sekvence dat. Proud je definován svým vstupem a výstupem, těmi mohou být například soubor na disku, zařízení (vstupní klávesnice nebo myš, výstupní displej) nebo jiný program. Primitivní proudy pouze přenášejí binární data, specializované proudy umí s přenášenými daty manipulovat. Tyto manipulační schopnosti se liší podle typu přenášených dat.
Datové proudy mohou být potenciálně nekonečné (tzv. codata), čímž se liší od běžných konečných dat.
Přehled
Proudy vznikly jako náhrada rigidního propojení procesů a zařízení v Unixu v 80. letech 20. století. Před zavedením proudů se programy musely explicitně připojovat k zařízením a programům. Programátor musel brát v úvahu různá rozhraní, různou reprezentaci dat. Datové proudy umožnily od těchto problémů abstrahovat vytvořením jednotného rozhraní.
Dennis Ritchie popisuje proudy jako plně duplexní (obousměrné) spojení mezi uživatelským procesem a zařízením popř. pseudozařízením. Skládá se z několika lineárně spojených zpracovávajících modulů; je analogický k Shell pipeline, přičemž data mohou proudit oběma směry. Moduly v proudu mezi sebou komunikují prostřednictvím zpráv. Na konci proudu, který je blíže procesu, je sada funkcí, které zprostředkovávají rozhraní se zbytkem systému. Požadavky uživatele na zápis a ovládání vstupů a výstupů jsou transformovány ve zprávy a odeslány proudu, požadavky na čtení přebírají data z proudu a předávají je uživateli. Na opačném konci proudu se nachází ovladač zařízení. Data přicházející z proudu jsou předávána zařízení a znaky a stavy zaznamenané zařízením jsou zabaleny do zpráv a odeslány přes proud směrem k uživatelskému programu. Další moduly, které zapojeny do proudu, mohou s procházejícími daty manipulovat.
Každý modul zapojený v proudu je složen ze dvou front, každou pro jeden směr. Kromě dat jsou s frontami spojeny i procedury put (vlož), která vkládá data do fronty, a service (obsluž), která je volána kdykoli jsou k dispozici data ke zpracování.
Proudy byly implementovány v UNIXu v roce 1984. Zároveň byly v jazyce C implementovány potřebné funkce a typy, které umožnily s datovými proudy pracovat.[1]
Standardní proudy
Systém vytváří ke spouštěným procesům tři popisovače, ke kterým se programy připojují prostřednictvím proudů. Jeden standardní vstup (0), jeden standardní výstup (1) a jeden chybový výstup (2). Pokud je proces spuštěn v terminálu, standardním vstupem je text napsaný do okna terminálu. U textových zpráv z programu je terminálové okno naopak příjemcem. Tyto proudy je možno přesměrovat. Procesu je možno operátorem ‚<‘ nastavit standardní vstup tak, aby četl například ze souboru.
more < vstup.txt
Analogicky lze výstup programu přesměrovat operátorem ‚>‘. Pro přesměrování standardního chybového výstupu je třeba uvést před operátorem číslo popisovače, tedy 2.
ping seznam.cz > ping_seznam.txt
Proudy v Javě
V jazyku Java existuje několik tříd, které se zabývají přenosem různých typů dat. Většina z nich se nachází v balíčku (package) java.io. Třídy pro práci se soubory se nacházejí v balíčku java.nio.file.[2]
Bajtové proudy
Všechny vstupní třídy jsou potomky třídy InputStream a výstupní třídy potomky OutputStream. Nejzákladnějším typem proudu je bajtový proud. Tento proud čte nebo zapisuje data na nízké úrovni po 8bitových svazcích. Doporučuje se tento typ proudu nepoužívat, protože existují specializovanější alternativy (viz dále).
Příklad
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; [[public]] [[Třída (programování)|class]] ZkopírujBajty { public static [[void]] main(String[] args) throws [[Výjimka (programování)|IOException]] { FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream("vstup.txt"); //otevři vstupní proud out = new FileOutputStream("vystup.txt"); //otevři výstup.proud int c; //právě čtený bajt; pokud je -1, je konec souboru while ((c = in.read()) != -1) { //dokud jsou data na vstupu out.write(c); //pošli je na výstup } } finally { if (in != null) { //pokud je otevřený vstupní proud in.close(); //uzavři ho } if (out != null) { //pokud je otevřený výstupní proud out.close(); //uzavři ho } } } }
Uzavírání proudů
V tomto případě je čten obsah souboru vstup.txt bajt po bajtu a ukládán do souboru vystup.txt. Oba proudy je třeba po použití uzavřít, protože těchto proudů může operační systém poskytnout omezené množství.
Znakové proudy
Java používá pro vnitřní reprezentaci znaků kódování Unicode. Znakové proudy s tím počítají a dokážou toto kódování při zápisu a čtení překládat na jiná. Všechny vstupní třídy jsou potomky třídy Reader a všechny výstupní třídy jsou potomky třídy Writer.
Příklad
import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class ZkopírujZnaky { public static void main(String[] args) throws IOException { FileReader in = null; FileWriter out = null; try { in = new FileReader("vstup.txt"); //otevři vstupní proud out = new FileWriter("výstup.txt"); //otevři výstupní proud int c; //právě čtený znak (16 bitů), -1 znamená konec souboru while ((c = in.read()) != -1) { //dokud jsou data na vstupu out.write(c); //zapiš znak na výstup } } finally { if (in != null) { //pokud je otevřený vstupní proud in.close(); //uzavři ho } if (out != null) { //pokud je otevřený výstupní proud out.close(); //uzavři ho } } } }
Proudy s vyrovnávací pamětí
Běžně je každý požadavek na zápis nebo čtení zpracováván přímo operačním systémem. To může vést k neefektivitě programu, protože každý takový požadavek může potřebovat např. přistupovat k disku nebo síťovým zdrojům, což jsou časově relativně náročné operace. Vyrovnávací paměť (buffer) je místo v operační paměti vyhrazené pro dočasné uchování dat před jejich přesunem na jiné místo. Pouze pokud je vyrovnávací paměť prázdná, použije se běžný přístup. Proudy lze „naučit“ používat vyrovnávací paměť využitím návrhového vzoru dekorátor. Proud, který čte ze souboru a využívá vyrovnávací paměť je možno vytvořit následovně:
inputStream = new BufferedReader(new FileReader("vstup.txt"));
Nejprve se vytvoří proud pro čtení ze souboru (FileReader) a ten poté „obalí“ proud pracující s vyrovnávací pamětí (BufferedReader). Obdobně pracuje i výstupní proud:
outputStream = new BufferedWriter(new FileWriter("characteroutput.txt"));
V Javě existují celkem 4 třídy, které obalují proudy: BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter.[3]
Vyprázdnění vyrovnávací paměti
Je dobrým zvykem vyprazdňovat vyrovnávací paměť (zapsat změněná data) na kritických místech. Tato operace uloží celý obsah vyrovnávací paměti na cíl proudu (soubor, zařízení atd.) Běžně se používá anglický výraz „flush“.
Dekorátor
Dekorátor je návrhový vzor, který se používá k obohacení existujících objektů o přidanou funkcionalitu. Funkce, které neupravuje, deleguje na obalený objekt. V angličtině se člověk může setkat s označením „wrapper“. Uplatnění tento vzor najde třeba při tvorbě GUI, kdy lze například podkladovou třídu
Okno o = new Okno();
obohatit o titulek tak, že
o = new OknoSTitulkem(o);
a dále třeba o posuvník
o = new OknoSPosuvníkem(o);
Datové proudy
Java obsahuje třídy pro pohodlné čtení a zápis primitivních datových typů a typu String. Nejrozšířenější třídy jsou DataInputStream a DataOutputStream. String je ukládán v kódování UTF-8. Údaje takto uložené např. do souboru nejsou uživatelsky přívětivé a s výjimkou řetězců čitelné. Metody pro čtení jsou readDouble, readInt, readUTF. Obdobně metody pro zápis mají předponu write.
Objektové proudy
Většina standardních tříd implementuje rozhraní Serializable (serializovatelný), které je nezbytné pro jejich podporu objektovými proudy. Objektové proudy rozšiřují datové proudy, takže objektové proudy umí pracovat i s primitivními datovými typy. Nové metody jsou readObject a writeObject. Pokud metoda readObject vrátí jiný než očekávaný objekt, vyhodí výjimku typu ClassNotFoundException.
Pokud se objekt neskládá jen z primitivních typů ale i z referencí na další objekty, je potřeba zachovat tyto reference. Proto je při zápisu objektu uložit i všechny objekty, na které má daný objekt odkaz. Podobně se bude chovat čtecí proud, který se bude snažit zrekonstruovat celou takovou síť objektů.[4]
Odkazy
Reference
- RITCHE, Dennis. A Stream Input-Output System [online]. AT&T, October, 1984. Dostupné online.
- Oracle. Lesson: Basic I/O [online]. Oracle, 2015. Dostupné online.
- Oracle. Buffered Streams [online]. Oracle, 2015. Dostupné online.
- Oracle. Object Streams [online]. Oracle, 2015. Dostupné online.
Literatura
- Pecinovský, Rudolf. Návrhové vzory. Brno : COMPUTER PRESS, 2007. 978-80-251-1582-4.