Java (15) - I/O operace I.
Jednou z klíčových činností v programech je výměna dat s okolním
světem. V Javě to jde velice snadno a elegantně.
1.6.2005 15:00 |
Lukáš Jelínek
| Články autora
| přečteno 49064×
Základní informace o streamech
Vstupně/výstupní operace lze v Javě realizovat několika způsoby. My se nyní
zaměříme na ten základní způsob, tedy práci se streamy. Téměř každý, kdo
programuje v nějakém jazyce, bude pojem stream znát. A právě v Javě se tomuto
slovu dostává plného významu.
Stream si lze představit jako trubku, jejíž konec máme k dispozici a můžeme
"čerpat" data z něho (tedy číst) nebo naopak do něho (tj. zapisovat).
Streamů existuje (z hlediska implementace) celá řada, z pohledu uživatele
se však všechny streamy daného směru (tedy "dovnitř" nebo "ven") chovají
skoro stejně - většinu funkcionality mají společnou.
Pro streamy je charakteristická především jejich sekvenčnost. I když to
neplatí úplně stoprocentně (existují streamy se "zpětným chodem"), s daty se
pracuje v konkrétním neměnném pořadí.
Streamy v Javě
V Javě je každý stream reprezentován jako objekt, tedy instance nějaké třídy.
Balík java.io obsahuje hierarchii základních tříd, se kterými si pro běžné
operace vystačíme (další streamy najdeme i v jiných standardních balících).
Pokud budeme potřebovat něco speciálního, není žádný
problém si vhodnou třídu vytvořit (resp. odvodit od nějaké existující).
Známe dvě hlavní kategorie streamů: binární a textové. Liší se způsobem práce
se znaky, binární streamy pracují se "surovými" bajty (tak, jak jsou), zatímco
streamy textové pojímají bajty, resp. skupiny bajtů způsobem, který odpovídá
nastavení prostředí.
Binární vstupní streamy jsou odvozeny od abstraktní třídy InputStream ,
výstupní od třídy OutputStream . Textové pak od třídy
Reader , resp. Writer .
Základy práce se streamy
Každý stream má svůj životní cyklus - je velmi jednoduchý a sestává z těchto
etap:
-
Vytvoření (zavolání konstruktoru) - může se vytvářet přímo tam, kde se
používá, anebo ho může vytvořit nějaký jiný objekt a předat.
-
Otevření - stream se často otvírá už při vytváření, pokud však otevřen nebyl,
musí se otevřít později, jinak s ním nelze pracovat. Otevření znamená, že
se alokují potřebné systémové prostředky a stream se připraví pro práci.
-
Vlastní práce - se streamem se provádějí požadované operace, tzn. volají se
jeho metody.
-
Uzavření - toto je velmi důležité, bohužel se na to často zapomíná. I když
korektně finalizovaný objekt streamu zaručuje správné zavření (u standardních
streamů), podobně i normální ukončení programu, nikdy se na to nesmí spoléhat.
Jednak se vyčerpávají systémové prostředky (file deskriptory apod.), a za druhé
může mnoho dat zůstat nezapsaných (u výstupních streamů), a to i hodně dlouho.
Streamy se prostě musí uzavírat, a to ihned, když s nimi přestáváme pracovat
(s jednou výjimkou, kterou za chvíli zmíním).
Ošetření chyb
Důležitým aspektem práce se streamy je ošetření chyb, které se mohou
vyskytnout. Téměř všechny chybové stavy jsou řešeny výjimkami. Tou základní
je IOException , kterou mohou vyhodit naprostá většina streamových
metod. Tato výjimka je synchronní (musí se tedy povinně zachytávat nebo deklarovat
k předání výše), zahrnuje do sebe celou škálu podtříd (výjimek signalizujících
konkrétní chybové stavy) a je společná všem streamům bez ohledu na implementaci.
Kromě IOException mohou streamy produkovat i jiné výjimky, to ale závisí na
jejich určení a implementaci.
Jak jsem již uvedl, výjimku IOException může vyhodit prakticky kterákoli
metoda streamu. Musíme to mít na zřeteli, ani takové uzavírání streamu není
"bezpečná" operace. Když se na to zapomene, připomene to rázně kompilátor.
Základní druhy streamů
Se streamy pracujeme prakticky stejně, ať už se jedná o data v souborech na
disku, o síťovou komunikaci, komunikaci mezi vlákny apod. I když je tato
práce téměř shodná, existují podstatné rozdíly zejména v přípravě streamů
před komunikací. Podíváme se tedy blíže na jednotlivé druhy streamů.
Souborové streamy
Asi nejčastějším způsobem komunikace s vnějším prostředím je čtení a zápis
souborů. Z hlediska Javy se nerozlišují vlastnosti souborového systému,
se soubory se pracuje vždy stejně, jen nás zajímá, zda soubor existuje,
lze z něj číst nebo do něj zapisovat.
try {
InputStream is = new FileInputStream("soubor.dat"); // stream se hned otevře
int i = 0;
while ((i = is.read()) >= 0) { // čte se, dokud není konec souboru
...
}
is.close(); // zavření souboru
} catch (IOException e) {
... // zpracování výjimky
}
Příklad ukazuje základní způsob čtení ze streamu, v tomto případě souborového.
Stream se otevře, v cyklu se z něho čte po bajtech (pozor - i když se čtou
bajty, hodnota je typu int ; je to z více důvodů, ale důležité je, že pokud
je přečtena hodnota -1, bylo dosaženo konce souboru). Všimněte si, že všechny
operace jsou uzavřeny do bloku try k zachycení výjimek.
Řetězcové streamy
Podobně jako třeba v C++, i v Javě lze snadno číst z řetězce a zapisovat do
něj streamovým způsobem. Stream pracuje nad objektem typu StringBuffer , ke
kterému můžeme získat přímý přístup - lze ale také ze streamu "vytáhnout"
také instanci třídy String , ta se ale samozřejmě musí vždy vytvořit, neboť
je neměnná.
Následující příklad ukazuje, jak se streamově zapisuje do řetězce. Stream je
samozřejmě textově orientovaný, což je naprosto v souladu s daným účelem.
Pro výstupní řetězcové streamy je charakteristické, že operace nevyhazují výjimku
IOException - a to ani při pokusu dělat nějaké operace po uzavření
streamu (operace uzavření totiž nic nedělá).
StringWriter sw = new StringWriter();
sw.write("abcd");
sw.write(sw.toString()); // obsahu streamu se zapíše zpět do streamu
System.out.println(sw);
Copak asi uvedený příklad dělá? Zapíše uvedený řetězec dvakrát za sebou
(nejprve přímo, potom prostřednictvím metody toString() zavolané na streamu)
a celý obsah vypíše na standardní výstup. Zbývá si ještě zodpovědět
jednoduchou otázku, k čemu je to vlastně dobré - samozřejmě hlavně k tomu, že
vytvořený stream můžeme předat k nějakému vnějšímu použití, kde nezáleží na
tom, kam se data zapisují (resp. odkud se čtou).
Mezi základní streamy patří ještě některé další druhy, ale o těch si řekneme
až později. Nejdřív by se totiž hodilo znát něco jiného...
Filtrové streamy
Největší množství streamů patří do obrovské množiny, které se říká filtrové
streamy. Takový stream si lze představit skutečně jako nějaký kus trubky
s filtrem. Nejobecněji to vypadá tak, že tento stream napasujeme na nějaký
jiný stream. Data, která přes filtrový stream procházejí, mohou být různě
pozměněna, stream je může všelijak zkoumat a něco počítat atd. Filtrové
streamy lze prakticky libovolně řetězit za sebe (pokud na sebe navazují
stejné kategorie, ve smyslu binární a textové).
Filtrových streamů je celá řada, řekneme si tedy nejprve o těch
nejdůležitějších.
Bufferované streamy
Protože často pracujeme s malými objemy dat, nebývají vstupně/výstupní operace
příliš operačně výkonné. Záleží na prostředcích operačního systému, druhu
streamu atd., a většinou nemůžeme na nic spoléhat (píšeme platformově
nezávislé programy!). Proto existují streamy, které obsahují vlastní buffer
a optimalizují přístupy k datům.
Představme si, že zapisujeme data třeba po jednom bajtu - to by za normálních
okolností mohlo znamenat třeba mnoho zbytečných přístupů na disk, posílání
"prázdných" paketů po síti apod. Přitom obvykle není žádný důvod, aby se
data okamžitě sunula někam dál. Pro optimalizaci tedy použijeme bufferovaný
stream.
try {
BufferedReader br = new BufferedReader(new FileReader("soubor.txt"));
String s = "";
while ((s = br.readLine()) != null) {
...
}
br.close();
} catch (IOException e) {
...
}
Příklad ukazuje hned několik aspektů práce s bufferovaným (textovým) streamem.
Filtrové streamy obvykle vytváříme tak, že předáme jejich konstruktoru
jako parametr podřízený stream, v daném případě textový souborový.
Dále je vidět, že zde můžeme číst celé řádky - to je věc specifická právě pro
tento stream, ale i zde můžeme stále číst jednotlivé znaky, toto je jen
usnadnění. Po skončení práce uzavřeme "nejvrchnější" stream, ten už zajistí
kaskádovitě uzavření všech ostatních.
Ještě důležitá poznámka - při použití bufferovaných výstupních streamů není
zaručeno, kdy se data z bufferu přesunou do navazujícího streamu. K zajištění
zápisu dat z bufferu proto v případě potřeby voláme metodu flush() .
Konverzní streamy
Název není úplně přesný, řeč bude pouze o konverzi mezi textovými a binárními
streamy. V řadě případů totiž odněkud získáme binární stream, a přitom
potřebujeme textový. Vřadíme tedy mezičlánek, který nám konverzi zajistí.
Viz příklad:
try {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int i = 0;
while (i >= 0) {
i = br.read();
if (i >= 0) {
char c = (char) i;
...
}
}
br.close();
} catch (IOException e) {
...
}
V příkladu je použit standardní vstup (čili obvykle klávesnice), který však
je binární. Protože potřebujeme znaky, musíme provést převod, a to nejlépe
hned "na cestě", pomocí konverzního streamu.
Streamy pro kompresi/dekompresi dat
Jako takovou třešničku na dortu, která ukáže, co se také se streamy dá dělat,
si nyní vyzkoušíme dekompresi dat a zároveň výpočet kontrolního součtu.
Na samém konci budeme číst po řádcích textová data.
Stačí jen streamy zřetězit za sebe...
try {
CheckedInputStream cis =
new CheckedInputStream(new GZipInputStream(new FileInputStream("data.gz")));
BufferedReader br = new BufferedReader(new InputStreamReader(cis));
String s = "";
while ((s = br.readLine()) != null) {
...
}
System.out.println("Kontrolni soucet je: " + cis.getValue());
br.close();
} catch (IOException e) {
...
}
Jak snadné... A tohle zdaleka není všechno, co streamy dovedou. Příště se
podíváme na některé další druhy streamů (těch zajímavých je ještě řada),
řekneme si něco o tom, jak efektivně a bezpečně přenášet různá data, a také
jak si vyrobit vlastní stream pro specifické účely.
Verze pro tisk
|
Příspívat do diskuze mohou pouze registrovaní uživatelé.
|
|

Vyhledávání software

Vyhledávání článků
28.11.2018 23:56 /František Kučera Prosincový sraz spolku OpenAlt se koná ve středu 5.12.2018 od 16:00 na adrese Zikova 1903/4, Praha 6. Tentokrát navštívíme organizaci CESNET. Na programu jsou dvě přednášky: Distribuované úložiště Ceph (Michal Strnad) a Plně šifrovaný disk na moderním systému (Ondřej Caletka). Následně se přesuneme do některé z nedalekých restaurací, kde budeme pokračovat v diskusi.
Komentářů: 1
12.11.2018 21:28 /Redakce Linuxsoft.cz 22. listopadu 2018 se koná v Praze na Karlově náměstí již pátý ročník konference s tématem Datová centra pro business, která nabídne odpovědi na aktuální a často řešené otázky: Jaké jsou aktuální trendy v oblasti datových center a jak je optimálně využít pro vlastní prospěch? Jak si zajistit odpovídající služby datových center? Podle jakých kritérií vybírat dodavatele služeb? Jak volit vhodné součásti infrastruktury při budování či rozšiřování vlastního datového centra? Jak efektivně datové centrum spravovat? Jak co nejlépe eliminovat možná rizika? apod. Příznivci LinuxSoftu mohou při registraci uplatnit kód LIN350, který jim přinese zvýhodněné vstupné s 50% slevou.
Přidat komentář
6.11.2018 2:04 /František Kučera Říjnový pražský sraz spolku OpenAlt se koná v listopadu – již tento čtvrtek – 8. 11. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma umění a technologie, IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář
4.10.2018 21:30 /Ondřej Čečák LinuxDays 2018 již tento víkend, registrace je otevřená.
Přidat komentář
18.9.2018 23:30 /František Kučera Zářijový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 20. 9. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář
9.9.2018 14:15 /Redakce Linuxsoft.cz 20.9.2018 proběhne v pražském Kongresovém centru Vavruška konference Mobilní řešení pro business.
Návštěvníci si vyslechnou mimo jiné přednášky na témata: Nejdůležitější aktuální trendy v oblasti mobilních technologií, správa a zabezpečení mobilních zařízení ve firmách, jak mobilně přistupovat k informačnímu systému firmy, kdy se vyplatí používat odolná mobilní zařízení nebo jak zabezpečit mobilní komunikaci.
Přidat komentář
12.8.2018 16:58 /František Kučera Srpnový pražský sraz spolku OpenAlt se koná ve čtvrtek – 16. 8. 2018 od 19:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát jsou tématem srazu databáze prezentaci svého projektu si pro nás připravil Standa Dzik. Dále bude prostor, abychom probrali nápady na využití IoT a sítě The Things Network, případně další témata.
Přidat komentář
16.7.2018 1:05 /František Kučera Červencový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 19. 7. 2018 od 18:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát bude přednáška na téma: automatizační nástroj Ansible, kterou si připravil Martin Vicián.
Přidat komentář
Více ...
Přidat zprávičku
 Poslední diskuze
31.7.2023 14:13 /
Linda Graham iPhone Services
30.11.2022 9:32 /
Kyle McDermott Hosting download unavailable
13.12.2018 10:57 /
Jan Mareš Re: zavináč
2.12.2018 23:56 /
František Kučera Sraz
5.10.2018 17:12 /
Jakub Kuljovsky Re: Jaký kurz a software by jste doporučili pro začínajcího kodéra?
Více ...
|