LINUXSOFT.cz Přeskoč levou lištu

ARCHIV



   

> 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 48435×

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:

  1. 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.
  2. 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.
  3. Vlastní práce - se streamem se provádějí požadované operace, tzn. volají se jeho metody.
  4. 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

pridej.cz

 

DISKUZE

Streamy 1.6.2005 22:11 Petr Zajíc




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 ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2024) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze