Image
22.6.2016 0 Comments

Java pod lupou I. /12.časť

Späť na úvod >> Späť na programovanie >> Späť na seriál

Vstup a výstup údajov

V dvanástom pokračovaní seriálu sa oboznámime s triedami, ktoré Java poskytuje na realizáciu vstupných a výstupných operácií. Na príklady, bohužiaľ, tentoraz nezostalo miesto, niekoľko ukážkových programov však možno nájsť na webe PC REVUE v sekcii Programujeme.

Triedy File a FileDescriptor

Trieda File navzdory svojmu menu neslúži na reprezentáciu súborov, ale na abstraktnú, systémovo nezávislú reprezentáciu ciest k súborom a/alebo adresárom. Cesta sa skladá z nepovinného prefixu (napr. \ v Unixe, C:\ alebo \\ vo Windows) a z postupnosti mien adresárov. Posledné meno môže predstavovať adresár alebo súbor. Oddeľovač adresárov je systémovo závislý a získať ho možno zo statického člena separator (typu String), resp. separatorChar (typu char) triedy File. Ďalšie dva statické členy, pathSeparator a pathSeparatorChar, obsahujú znak oddeľujúci zoznam ciest (napríklad v premennej prostredia PATH). Cesta môže byť absolútna i relatívna a môže obsahovať špeciálne adresáre . a .. (t. j. aktuálny adresár a nadradený adresár).

Konštruktory triedy File akceptujú jeden alebo dva reťazcové argumenty, ktoré obsahujú cestu (pri dvoch argumentoch sa cesty jednoducho spoja – ak pracujeme so súbormi v nejakom adresári, môže byť vďaka tomu cesta k adresáru a cesta ku konkrétnemu súboru uložená každá v samostatnej premennej). V tretej verzii konštruktora možno namiesto prvého z reťazcov použiť inštanciu triedy File.

V deklarácii triedy File ďalej nájdeme niekoľko informačných metód so samovysvetľujúcimi názvami: getName(), getParent(), getPath(), getAbsolutePath(), getCanonicalPath() a iné (kanonická cesta je cesta, ktorá neobsahuje adresáre . a ..). Pomocou metód canRead() a canWrite() môžeme zistiť, či má program právo na čítanie/zápis z/do súboru. Metódy exists(), isDirectory(), isFile(), isHidden() zisťujú, či daná cesta existuje, či reprezentuje adresár, resp. súbor a či je skrytá (skrytá cesta má vo Windows nastavený atribút Hidden, v Unixe sa začína bodkou). Metóda lastModified() vracia čas poslednej modifikácie daného objektu, length() zase jeho dĺžku v bajtoch.

Metóda delete() slúži na vymazanie súboru/adresára, renameTo() na ich premenovanie. Nový adresár vytvoríme pomocou metód mkdir() alebo mkdirs(). Metódy list() a listFiles() poskytujú zoznam všetkých súborov/adresárov, ktoré sa nachádzajú vo vybranom adresári (tento výber možno vhodne filtrovať), pomocou metód setLastModified() a setReadOnly() zmeníme čas poslednej modifikácie súboru či nastavíme príznak read-only. Statická metóda createTempFile() príde vhod, ak potrebujeme vytvárať dočasné súbory; o ich automatické zmazanie po skončení programu sa postará metóda deleteOnExit().

Trieda FileDescriptor je objektovým zapuzdrením tradičného súborového deskriptora (celé číslo, identifikujúce otvorený súbor na úrovni operačného systému). Inštanciu triedy FileDescriptor možno získať volaním metódy getFD() nad objektom niektorej z tried FileInputStream, FileOutputStream alebo RandomAccessFile (pozri nižšie). Trieda obsahuje metódu valid(), ktorá zisťuje platnosť deskriptora súboru, a metódu sync(), ktorá vynúti synchronizáciu systémových vyrovnávacích pamätí so záznamovým médiom.

I/O triedy

V Jave sa vstupné/výstupné operácie realizujú podobne ako v C/C++ pomocou prúdov (streams). Prúd je údajový tok, ktorý smeruje od zdroja údajov do programu (vstupný prúd), resp. z programu do spotrebiča údajov (výstupný prúd). Zdrojom/spotrebičom údajov môže byť diskový súbor, konzola (= klávesnica/obrazovka), IPC rúra, sieťový soket a podobne. Výhodou prúdov je unifikovaný spôsob ich obsluhy. Koreňmi objektovej hierarchie I/O tried sú štyri abstraktné triedy: InputStream, OutputStream, Reader a Writer. Prvá dvojica predstavuje spoločný základ pre bajtové prúdy (t. j. postupnosť bajtov), druhá dvojica pre znakové prúdy (postupnosť znakov).

Trieda InputStream

Trieda InputStream obsahuje abstraktnú metódu read() v troch obmenách, ktorá slúži na čítanie bajtu alebo poľa bajtov zo vstupného prúdu. Odvodené triedy túto metódu musia vhodne implementovať. Ak v prúde nie sú k dispozícii žiadne údaje, volanie read() je blokujúce (okrem prípadu, keď sa dosiahne koniec prúdu). Pomocou metódy skip() možno určitý počet bajtov v prúde „preskočiť“. Metóda available() indikuje, či sa v prúde nachádzajú nejaké neprečítané dáta, a pomocou metód mark() a reset() si možno „poznačiť“ pozíciu v prúde a neskôr sa k nej vrátiť. Túto funkčnosť však nemusí poskytovať každý prúd. Poslednou metódou je close(), ktorá slúži na korektné ukončenie práce s prúdom.

Pozrime sa teraz na potomkov triedy InputStream. Trieda ByteArrayInputStream realizuje čítanie bajtov z bajtového poľa. Trieda FileInputStream umožňuje sekvenčne čítať bajty z diskového súboru. Argumentom jej konštruktora je cesta k súboru (typu String alebo File). Trieda ObjectInputStream slúži na deserializáciu objektov – táto téma je s ohľadom na svoju zložitosť mimo rozsahu seriálu. Pomocou triedy PipedInputStream možno vytvoriť rúru (pipe) na medziprocesovú (resp. medzithreadovú) komunikáciu. Druhú stranu rúry, čo musí byť inštancia triedy PipedOutputStream, možno zadať ako argument konštruktora alebo ako argument samostatnej metódy connect(). Trieda SequenceInputStream umožňuje zreťaziť dva a viac vstupných prúdov do jedného.

Dôležitým potomkom triedy InputStream je trieda FilterInputStream, ktorá umožňuje podľa potreby filtrovať iný vstupný prúd (ten sa jej pošle ako argument konštruktora). Samotná trieda FilterInputStream prepúšťa všetky bajty bez zmeny, skutočnými filtrami sú až jej potomkovia: trieda BufferedInputStream, ktorá obsahuje vyrovnávaciu pamäť (buffer) a tým zefektívňuje čítanie údajov zo zdrojového prúdu, trieda PushbackInputStream, ktorej metódu unread() môžeme použiť na „vrátenie“ posledného načítaného bajtu späť do prúdu, a predovšetkým trieda DataInputStream, pomocou ktorej možno čítať z prúdu primitívne javovské typy strojovo nezávislým spôsobom (existuje jednoznačné mapovanie každého primitívneho typu na postupnosť bajtov a naopak), prostredníctvom metód readBoolean(), readByte(), readUnsignedByte(), readShort(), readUnsignedShort(), readChar(), readInt(), readLong(), readFloat(), readDouble() a readUTF().

Uvedené triedy možno medzi sebou vhodne kombinovať, takže ak chceme napríklad čítať hodnoty primitívnych typov zo súboru a využívať pritom vyrovnávaciu pamäť, použijeme nasledujúci zápis:

FileInputStream f = new FileInputStream("file.dat");
BufferedInputStream b = new BufferedInputStream(f);
DataInputStream d = new DataInputStream(b);

Každý ďalší prúd slúži ako wrapper (v slovenskom preklade azda „zapuzdrovač“) predchádzajúceho prúdu. S finálnym objektom d pracujeme štandardným spôsobom.

Trieda OutputStream

Trieda OutputStream je výstupným náprotivkom triedy InputStream. Je takisto abstraktná a obsahuje kľúčovú metódu write(), realizujúcu zápis bajtu alebo poľa bajtov do výstupného prúdu, metódu flush() na vyprázdnenie prípadnej vnútornej vyrovnávacej pamäte a metódu close(), ktorá výstupný prúd uzavrie.

Triedy odvodené od OutputStream sú poväčšine výstupnými ekvivalentmi potomkov triedy InputStream. Trieda ByteArrayOutputStream ako spotrebič údajov využíva pole bajtov, ktoré sa dokáže dynamicky zväčšovať. Pomocou metódy writeTo() možno toto pole odoslať do iného prúdu a pomocou toByteArray() previesť na samostatný objekt typu byte[]. Trieda FileOutputStream slúži na zápis bajtov do diskového súboru. Argumentom jej konštruktora môže byť reťazec, objekt triedy File alebo objekt triedy FileDescriptor. V prvom prípade možno pomocou dodatočného argumentu špecifikovať, či sa má súbor otvoriť v móde append (t. j. zapisované bajty sa budú pridávať na koniec už existujúceho súboru. Pomocou triedy ObjectOutputStream možno realizovať serializáciu objektov. Na vytvorenie zápisovej zložky medziprocesovej komunikácie je k dispozícii trieda PipedOutputStream. K vytváraniu rúr na komunikáciu medzi threadmi sa ešte v seriáli vrátime pri rozprávaní o threadoch – na príklade budeme demonštrovať, ako sa rúra vytvorí a ako sa cez ňu posielajú údaje.

Aj medzi výstupnými prúdovými triedami nájdeme „filtrovaciu“ triedu FilterOutputStream. Tá sama osebe prepúšťa údaje bez zmeny. Medzi jej potomkov patrí trieda BufferedOutputStream, ktorá implementuje výstupný prúd s vyrovnávacou pamäťou, a takisto trieda DataOutputStream, pomocou ktorej možno do výstupného prúdu zapisovať hodnoty primitívnych typov (v binárnom formáte) a reťazcov – na to máme k dispozícii metódy writeBoolean(), writeByte(), writeShort(), writeChar(), writeInt(), writeLong(), writeFloat(), writeDouble(), writeBytes(), writeChars() a writeUTF(). Poslednou filtrovacou triedou je PrintStream, vďaka ktorej môžeme realizovať zápis hodnôt primitívnych typov (a reťazcov) v ľudskom, čitateľnom tvare, prostredníctvom metód print() a println(). Druhá z metód ukončuje výpis znakom prechodu na nový riadok, čo je obyčajne '\n'. Na ilustráciu rozdielu medzi DataOutputStream a PrintStream: v prvom prípade sa číslo 123 (nech je typu byte) zapíše do prúdu ako jeden bajt s hexadecimálnou hodnotou 0x7B, v druhom prípade ako tri ASCII znaky 1, 2 a 3, teda tri hexadecimálne hodnoty 0x31, 0x32 a 0x33.

Trieda Reader

Trieda Reader je prakticky totožná s triedou InputStream – je takisto abstraktná a obsahuje rovnaké metódy (s výnimkou metódy ready(), ktorá je však inak ekvivalentná metóde available()), jediným rozdielom je, že táto trieda z prúdu číta znaky. Tie, ako vieme, majú rozsah 16 bitov, čo sú dva bajty. Trieda Reader preto plne podporuje všetky unikódové znaky a nie je viazaná na vybrané osembitové kódovanie národných abecied.

Trieda Reader má množstvo potomkov, čiastočne podobných potomkom triedy InputStream. Nájdeme tu tak triedu CharArrayNumber, ktorá ako zdroj údajov používa pole znakov, triedu StringReader, ktorá podobným spôsobom číta znaky z objektu typu String, i triedu PipedReader, ktorá umožňuje čítanie znakov z rúry IPC. Trieda InputStreamReader funguje ako most medzi bajtovými a znakovými prúdmi. Argumentom jej konštruktora je inštancia niektorého vstupného prúdového typu, z ktorého sa bajty čítajú a prekladajú na znaky podľa vybraného kódovania znakov. Táto trieda sa využije pri čítaní znakov zo štandardného vstupu, pretože statický člen System.in je typu InputStream. Na zjednodušenie práce so súbormi je od triedy InputStreamReader odvodená trieda FileReader, ktorá je v podstate spojením objektov typu InputStreamReader a FileInputStream.

Z filtrovacích tried tu nájdeme triedu FilterReader, ktorá opätovne slúži len ako bázová trieda, a jej potomka PushbackReader, schopného prostredníctvom metódy unread() vrátiť posledný načítaný znak späť do prúdu. Ďalšia trieda BufferedReader obsahuje vnútornú vyrovnávaciu pamäť, vďaka čomu môžeme naraz čítať celé riadky znakov – na to slúži metóda readLine(). Od triedy BufferedReader je odvodená jej špeciálna verzia LineNumberReader, ktorá prostredníctvom metódy getLineNumber() umožňuje zistiť poradové číslo aktuálneho riadka. Číslovanie možno zmeniť pomocou metódy setLineNumber().

Trieda Writer

Posledná zo štvorice, trieda Writer, je znakovým ekvivalentom triedy OutputStream. Obsahuje rovnaké metódy – menovite metódu write(), ktorá slúži na zápis znaku, poľa znakov alebo reťazca do výstupného prúdu, a metódy flush() a close() na vyprázdnenie, resp. uzavretie prúdu.

Medzi potomkami triedy Writer logicky nájdeme triedu CharArrayWriter, ktorá zapisuje znaky do znakového poľa (s možnosťou poslať toto pole do iného znakového prúdu pomocou metódy writeTo() alebo ho konvertovať na typ char[] pomocou metódy toCharArray()), podobnú triedu StringWriter, využívajúcu namiesto poľa znakov objekt typu String, ako aj triedu PipedWriter, ako protipól triedy PipedReader pri komunikácii prostredníctvom rúry. Prepojenie s bajtovými prúdmi zabezpečuje trieda OutputStreamWriter, ktorá zapisované znaky prekladá pomocou zvoleného kódovania na postupnosť bajtov a tie posiela na zadaný bajtový prúd. Táto trieda nájde pre zmenu uplatnenie pri práci so štandardnými výstupnými prúdmi System.out a System.err. Od triedy OutputStreamWriter je odvodená trieda FileWriter, ktorá je spojením objektov typu OutputStreamWriter a FileOutputStream.

Posledné tri triedy sú opäť filtrovacie: trieda FilterWriter (ako základ pre tvorbu vlastných filtrov), trieda BufferedWriter, zahŕňajúca vyrovnávaciu pamäť, a nakoniec trieda PrintWriter, ktorá má rovnakú funkciu ako trieda PrintStream, teda umožňuje prostredníctvom metód print() a println() zapisovať do výstupného prúdu formátovanú reprezentáciu hodnôt primitívnych typov a reťazcov.

Ďalšie triedy

Je len málo programov, ktoré počas svojho behu nepracujú s diskovými súbormi. Triedy z predchádzajúcich odsekov, pomocou ktorých možno čítať a zapisovať z/do súborov, umožňujú len sekvenčný prístup. Pomocou metód mark()/reset() a skip() toto obmedzenie možno aspoň čiastočne obísť, ale pre pohodlnú prácu so súbormi tak, ako sme zvyknutí napríklad z C/C++, je to málo. Preto v Jave nájdeme samostatnú triedu RandomAccessFile, ktorá implementuje možnosť práce so súborom, t. j. čítanie a zápis, formou náhodného prístupu. Slovko náhodný v tomto kontexte, pochopiteľne, znamená skôr ľubovoľný.

Inštanciu triedy RandomAccessFile si možno predstaviť ako virtuálne pole bajtov, z ktorého možno čítať a do ktorého možno zapisovať údaje (aj za koniec súboru, lebo v takom prípade sa súbor začne jednoducho predlžovať). Inštancia si pamätá aktuálnu pozíciu v súbore – tú, z ktorej sa bude nasledujúcou operáciou čítať alebo na ktorú sa bude zapisovať.

Argumenty konštruktora triedy RandomAccessFile sú dva: prvý typu String alebo File reprezentuje súbor, s ktorým sa bude pracovať, druhý typu String udáva režim práce: "r" znamená čítanie, "rw" je čítanie + zápis. Iné režimy neexistujú. Pri vytvorení inštancie sa súčasne vytvorí deskriptor súboru, objekt typu FileDescriptor, ktorý možno získať pomocou metódy getFD().

Na univerzálne čítanie bajtov zo súboru slúžia metódy read() a readFully(). Prvá z nich sa správa presne tak isto ako metóda read() triedy InputStream. Druhá metóda readFully() sa od read() líši v tom, že čaká, kým nie je k dispozícii presne požadovaný počet bajtov. Metóda skipBytes() umožňuje „preskočiť“ stanovený počet bajtov. Univerzálny zápis bajtov do súboru má na starosti metóda write(), analogická metóde write() triedy OutputStream. Na prácu s ukazovateľom aktuálnej pozície slúžia metódy getFilePointer() (zistenie) a seek() (nastavenie). Zistenie a nastavenie dĺžky súboru zabezpečujú metódy length() a setLength(). Súbor sa uzavrie volaním metódy close().

Metódy read() a write() sa na pohodlnú prácu príliš nehodia. Trieda RandomAccessFile preto obsahuje rovnaké metódy typu read***() a write***(), ako triedy DataInputStream a DataOutputStream – pozri vyššie.

V balíku java.io sa nachádza ešte jedna užitočná trieda, StreamTokenizer, ktorá umožňuje parsing (toto slovo sa mi nedarí vhodne preložiť – rozbor, analýzu?) vstupného údajového toku a jeho rozklad na postupnosť tzv. tokenov. (Čitateľ si azda spomenie na jednu z prvých častí tohto seriálu, v ktorej bola reč o lexikálnych jednotkách Javy. Postupnosť lexikálnych jednotiek v zdrojovom súbore je výborný príklad na postupnosť tokenov.) Na bližšie oboznámenie sa s triedou StreamTokenizer však niet v seriáli miesta. V budúcnosti sa stretneme s používanejšou triedou StringTokenizer – tam bude k dispozícii aj príklad.

Výnimky a rozhrania

Spoločným predkom všetkých výnimiek deklarovaných v balíku java.io je trieda IOException, ktorá signalizuje, že došlo k nejakej, bližšie nešpecifikovanej chybe pri I/O operácii. Medzi jej potomkov patria triedy CharConversionException (chyba pri konverzii znakov), EOFException (pri čítaní údajov zo súboru/prúdu signalizuje jeho koniec), FileNotFoundException (nastane pri pokuse o otvorenie neexistujúceho súboru), InterruptedIOException (pri ukončení threadu vykonávajúceho I/O operáciu signalizuje jej prerušenie), ObjectStreamException (bázová trieda pre výnimky týkajúce sa serializácie objektov – sama osebe má množstvo ďalších potomkov), SyncFailedException (chyba pri synchronizácii počas volania metódy sync()), UnsupportedEncodingException (pokus o použitie nepodporovaného kódovania znakov) a nakoniec UTFDataFormatException (vyskytne sa pri načítaní neplatnej UTF-8 sekvencie zo vstupného prúdu; UTF-8 je jeden zo spôsobov kódovania šestnásťbitových unikódových znakov pomocou osembitových hodnôt).

V balíku java.io možno nájsť aj deklaráciu niekoľkých rozhraní: DataInput obsahuje všetky tie read***() metódy, ktoré implementujú triedy DataInputStream a RandomAccessFile, podobne rozhranie DataOutput obsahuje všetky metódy write***(), implementované triedami DataOutputStream a RandomAccessFile. Obe rozhrania majú aj potomkov, ObjectInput a ObjectOutput, zahŕňajúcich metódy pre čítanie a zápis objektov, polí a reťazcov. Ďalšie rozhrania Serializable, Externalizable, ObjectInputValidation a ObjectStreamConstants sa týkajú serializácie objektov, no a posledné dve rozhrania FileFilter a FilenameFilter obsahujú metódu accept() a používajú sa pri filtrovaní zoznamu súborov v metódach list() a listFiles() triedy File.

Nabudúce

V ďalšej časti seriálu sa pozrieme na obsah balíkov java.net a java.util. Dovtedy dovidenia.

 

 

 


Nechajte si posielať prehľad najdôležitejších správ emailom

Mohlo by Vás zaujímať

Ako na to

Tipy a triky: Ako na snímku obrazovky na akomkoľvek počítači s Windows?

02.12.2016 00:13

Ak snímky obrazovky robíte často apotrebujete napríklad funkcie na posun stránok alebo snímanie zobrazenia pri vyššom rozlíšení displeja, zrejme používate nejakú špecializovanú aplikáciu. Väčšina použ ...

Ako na to 1

Tipy a triky: Ako aplikácii prednastaviť spúšťanie s administrátorskými právami?

30.11.2016 00:10

Väčšina aspoň trochu skúsenejších používateľov vie, že aj keď máte na operačnom systéme Windows vytvorený administrátorský účet, aplikácie pre bezpečnosť nefungujú vždy splnými administrátorskými práv ...

Ako na to 2

Tipy a triky: Ako vypnúť uzamykaciu obrazovku vo Windows 10?

29.11.2016 00:10

Rozčuľuje vás, že pred každým prihlásením doúčtu vášho počítača musíte prejsť uzamykacou obrazovkou? Windows 10 na tejto obrazovke ukazuje čas,dátum anejakú zaujímavú fotografiu zrôznych kútov sveta. ...

Žiadne komentáre

Vyhľadávanie

Kyocera - prve-zariadenia-formatu-a4-s-vykonom-a3

Najnovšie videá