Diferență între revizuiri ale paginii „Input/Output Streams”
Linia 43: | Linia 43: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | Method ''read()'', with no arguments, reads the next byte from the stream and return it as an '''int''' with values between 0 and 255. If the end of stream has been reached, it returns -1. | |
− | + | Methods ''read()'', having as arguments an array of bytes, read a number of ''len'' or the length of the array ''b'' (whichever is smaller) of bytes from the stream and write them in array ''b'' at index ''off'', if this is specified. They return the number of bytes read from the stream (and subsequently written in ''b'') or -1 if the end of stream has been reached. | |
− | + | Method ''skip()'' is used to discard a number of bytes from the input stream, specified in argument ''n''. | |
− | + | Methods ''mark()'' and ''reset()'' are used together, so that portions of the stream that have been already read can be read again. Through method ''mark()'', the current position in the stream is memorized, specifying the maximum size of the buffer that will store the following data through the argument ''readLimit'' that can be written before calling ''reset()''. The ''reset()'' method re-position the read index to the position where it was when the ''mark()'' method was called. These two methods are only valid if the stream supports marking. Checking for this is done by calling method ''markSupported()''. | |
− | + | Method ''available()'' returns the number of bytes available to be read from the stream without blocking execution. Be aware that some classes (like file streams) only return an estimate value when calling ''available()''. Check the documentation. | |
− | + | Method ''close()'' is used to close a stream. Once work with a stream is done, it needs to be closed in order to free the associated resources. Normally, JVM will automatically close all streams then the program ends, but a programmer should not count on this feature and clean up after him/herself. | |
− | + | In order to use class '''InputStream''' in applications, it needs to be extended by classes that are connected to real data sources, existing in the system. Some of these classes, defined in Java, are: | |
− | * [http://docs.oracle.com/javase/7/docs/api/java/io/ByteArrayInputStream.html ByteArrayInputStream] | + | * [http://docs.oracle.com/javase/7/docs/api/java/io/ByteArrayInputStream.html ByteArrayInputStream] can read data from an array of bytes, acting as an input stream; |
− | * [http://docs.oracle.com/javase/7/docs/api/java/io/FileInputStream.html FileInputStream] oferă | + | * [http://docs.oracle.com/javase/7/docs/api/java/io/FileInputStream.html FileInputStream] oferă can read bytes coming from a file on the file system; |
− | * [http://docs.oracle.com/javase/7/docs/api/java/io/SequenceInputStream.html SequenceInputStream] | + | * [http://docs.oracle.com/javase/7/docs/api/java/io/SequenceInputStream.html SequenceInputStream] can combine multiple input streams in a single stream; |
− | * [http://docs.oracle.com/javase/7/docs/api/javax/sound/sampled/AudioInputStream.html AudioInputStream] | + | * [http://docs.oracle.com/javase/7/docs/api/javax/sound/sampled/AudioInputStream.html AudioInputStream] can read from an audio source. |
Versiunea de la data 15 decembrie 2013 11:15
A computer communicates with the exterior world through one or more communication interfaces, also called input-output (IO) ports. E.g.: serial port, parallel port, video port, audio port, etc. By using these interfaces, the computer is able to transmit and receive information to and from the outside world through different peripheral devices (monitor, printer, keyboard, mouse, modem, Ethernet card, etc.).
Input/Output Streams
Considering the large number of peripheral types, the constant development of new ones and also the fact that existing peripherals are being constantly upgraded, direct communication between them and the software programs would be difficult to achieve if it were to only use the processor instructions (in/out). Furthermore, the complexity of the communication with the peripheral devices gets even higher with different types of communication: sequential/ random, binary/ character/ line by line, etc. In order to help with these problems, device drivers were developed, which are software modules used to control the peripherals and provide a simpler communication interface, however, they don't completely solve the compatibility issues of the IO operations. As a result, the operating system also introduces a layer of abstraction, offering a more uniform way of transferring data to and from the peripheral devices. However, programming languages, like Java, are created so that can be used in several operating systems and so additional standard functions are required, defined in the programming language libraries, to communicate with devices, independently of the operating system.
In order to treat communication uniformly, Java uses the concept of stream. The notion has been invented by Dennis Ritchie, who implemented the first IO system based on streams, in the Unix operating system, in year 1984. A stream implies the creation of a communication channel, generally valid, between two entities, either software or hardware, with the condition that one entity is a source, and the other is a destination. So, the source will transmit (write) information in the stream, and the destination will receive (read) from the stream. This way, the stream allows treating any communication channel between any entities in the system in the same way. Defining a stream is done by defining its two ends (source and destination). Because there are two communication directions, we can separate streams into two categories: input streams and output streams. Input streams are streams that connect to devices that provides data (like keyboards) and output streams connect to devices that receive data (like the monitor). Both source and destination can be either a peripheral device or software component.
Console Streams
The Java language provides three types of standard streams for communicating with the console:
- standard input stream - type InputStream, used to read data;
- standard output stream - type PrintStream, used to display data;
- standard error stream - type PrintStream, used to display errors.
In Java, all standard streams are accessible through class java.lang.System. So, for standard input, there is System.in, for standard output there is System.out and for standard error, there is System.err. The three members in, out and err of class System are static.
Classes that implement IO operations are placed in package java.io. This package contains two categories of streams: bytes streams and character streams. Byte streams have been introduced from JDK version 1.0.2 and character streams are available since JDK 1.1. The latter ones do not replace, but complete stream functionality by being able to directly read and write characters (which in Java have 2 bytes each).
Byte Input/Output Streams
Bytes streams (as character streams) are split in two categories: input streams and output streams.
=== Bytes Input Streams
Classes implementing byte input streams are extended from java.io.InputStream, an abstract class which offers generic read methods. The definition of the class is:
public abstract class InputStream{
public abstract int read() throws IOException;
public int read(byte b[]) throws IOException;
public int read(byte b[], int off, int len) throws IOException;
public long skip(long n) throws IOException;
public int available() throws IOException;
public void close() throws IOException;
public synchronized void mark(int readlimit);
public synchronized void reset() throws IOException;
public boolean markSupported();
}
Method read(), with no arguments, reads the next byte from the stream and return it as an int with values between 0 and 255. If the end of stream has been reached, it returns -1.
Methods read(), having as arguments an array of bytes, read a number of len or the length of the array b (whichever is smaller) of bytes from the stream and write them in array b at index off, if this is specified. They return the number of bytes read from the stream (and subsequently written in b) or -1 if the end of stream has been reached.
Method skip() is used to discard a number of bytes from the input stream, specified in argument n.
Methods mark() and reset() are used together, so that portions of the stream that have been already read can be read again. Through method mark(), the current position in the stream is memorized, specifying the maximum size of the buffer that will store the following data through the argument readLimit that can be written before calling reset(). The reset() method re-position the read index to the position where it was when the mark() method was called. These two methods are only valid if the stream supports marking. Checking for this is done by calling method markSupported().
Method available() returns the number of bytes available to be read from the stream without blocking execution. Be aware that some classes (like file streams) only return an estimate value when calling available(). Check the documentation.
Method close() is used to close a stream. Once work with a stream is done, it needs to be closed in order to free the associated resources. Normally, JVM will automatically close all streams then the program ends, but a programmer should not count on this feature and clean up after him/herself.
In order to use class InputStream in applications, it needs to be extended by classes that are connected to real data sources, existing in the system. Some of these classes, defined in Java, are:
- ByteArrayInputStream can read data from an array of bytes, acting as an input stream;
- FileInputStream oferă can read bytes coming from a file on the file system;
- SequenceInputStream can combine multiple input streams in a single stream;
- AudioInputStream can read from an audio source.
O altă categorie importantă de clase derivate din InputStream, de data aceasta însă nu direct, sunt clasele de tip filtru derivate din clasa FilterInputStream care este la rândul ei derivată din clasa InputStream. Aceste tipuri de clase nu oferă nici o alta sursă pentru fluxuri față de clasele amintite anterior. Ele lucrează ca o înfășurătoare a claselor care se conectează direct la sursă, oferind acestora din urmă funcții (proprietăți) suplimentare de citire din flux (exemplu: citire pe linie, citire numere, etc). Ca exemple de astfel de clase amintim:
- DataInputStream este una din cele mai utilizate clase dintre cele de tip filtru. Conține mai multe funcții care permit citirea unor tipuri fundamentale de date (int, float, etc) într-un mod independent de sistem și de mașină (procesor);
- PushbackInputStream oferă posibilități de revenire, permițând citirea din nou a datelor deja citite.
În continuare este prezentat un exemplu de utilizare a clasei ByteArrayInputStream:
byte _buffer[] = new byte[10];
for(byte i = 0; i < _buffer.length; i++){
_buffer[i] = i;
}
ByteArrayInputStream _bufferStream = new ByteArrayInputStream(_buffer);
int _byteRead;
while((_byteRead = _bufferStream.read()) != -1){
System.out.print(” ” + _byteRead);
}
System.out.println();
Prin intermediul clasei ByteArrayInputStream se pot citi datele din tablou utilizând operațiile de citire din flux, fără a mai fi nevoie de a contoriza poziția de citire din tablou. Conectarea se face simplu prin transmiterea tabloului respectiv ca și parametru în constructorul fluxului. Exemplul construiește un tablou de 10 octeți, inițializându-l cu valori de la 0 la 9, după care se citesc aceste valori din tablou prin intermediul fluxului creat. Se observă că citirea din flux se face până în momentul în care funcția read() returnează –1, semnalizând că s-a ajuns la sfârșitul fluxului, deci la sfârşitul tabloului.
Fluxuri de ieșire orientate pe octet
Pentru scrierea datelor folosind conceptul de flux orientat pe octet, se folosește o ierarhie de clase derivată din clasa OutputStream. Ca și clasa InputStream, clasa OutputStream este o clasa abstractă, oferind o interfață de declarare a unor metode de scriere general valabile. Clasa are următoarea definiție:
public abstract class OutputStream{
public abstract void write(int i) throws IOException;
public void write (byte b[]) throws IOException;
public void write (byte b[], int off, int len) throws IOException;
public long flush() throws IOException;
public void close() throws IOException;
}
Funcţia write() scrie în flux octetul cel mai puțin semnficativ al argumentului, care este de tip int. Celelalte funcții permit scrierea în flux a unui tablou de octeți sau a unei secțiuni din tabloul de octeți b începând de la poziția off și având lungimea len.
Funcția flush() forțează scriea datelor în stream și este utilă în cazul în care se folosește un buffer de scriere (golește buffer-ul).
Funcția close() închide fluxul deschis prin operația de creare a fluxului, eliberând astfel orice resursă de sistem ocupată de fluxul respectiv.
Ca și în cazul clasei InputStream, clasele derivate din OutputStream pot fi separate în două categorii: fluxuri propriu-zise (ByteArrayOutputStream, FileOutputStream, etc) care se conectează la o destinație, și fluxuri de tip filtru (derivate din FilterOutputStream). Ultimele tipuri de fluxuri se conectează la primele, îmbogățind funcționarea acestora.
Fluxurile standard de ieșire (System.out) și de eroare (System.err) sunt membri statici ai clasei System de tipul PrintStream, care extinde clasa FilterOutputStream. Acestea sunt conectate cu display-ul și se folosesc prin convenție în două situații distincte: primul pentru afișarea mesajelor către utilizatorul programului, şi al doilea pentru afișarea mesajelor de eroare (de cele mai multe ori în scop de depanare).
În continuare este prezentat un exemplu de utilizare a clasei ByteArrayOutputStream, prin care se afișează toate valorile pe 8 biţi:
ByteArrayOutputStream _buffer = new ByteArrayOutputStream(256);
try{
for(int i=0;i<256;i++) {
_buffer.write(i);
}
_buffer.writeTo(System.out);
byte[] _tablou = _buffer.toByteArray();
}catch(IOException _e){
System.out.println("Eroare "+ _e.getMessage());
}
Clasa ByteArrayOutputStream este clasa corespondentă clasei ByteArrayInputStream și permite conectarea unui flux la un buffer (un tablou de octeți). Buffer-ul se crează în momentul în care un obiect de tipul ByteArrayOutputStream se instanțiază folosind unul dintre cei doi constructori. Această clasă oferă o metodă de stocare a datelor în memorie înainte de a le trimite mai departe. Pentru transmiterea datelor se folosește metoda writeTo(). Această funcție scrie conținutul buffer-ului în fluxul out transmis ca parametru. Spre deosebire de clasa ByteArrayInputStream unde tabloul (buffer-ul) era transmis prin constructorul clasei, în cazul clasei ByteArrayOutputStream, pentru a avea o referintă la acest buffer, este nevoie să se apeleze funcția toByteArray(). Această funcție creează un tablou de dimensiune egală cu numărul de octeți înscriși în flux și care conține acești octeți.
Fluxuri de intrare-ieșire la nivel de caracter
Clasele pentru fluxurile la nivel de caracter au la bază clasa Reader pentru citirea datelor și clasa Writer pentru scrierea datelor. Majoritatea acestor clase se suprapun peste clasele pentru fluxurile la nivel de octet, oferind în principal aceleași funcționalități. Deosebirea între clasele de tip InputStream/OutputStream și cele de tip Reader/Writer este aceea că primele clase lucrează cu fluxuri de octeți (8 biți), în timp ce ultimele lucrează cu fluxuri de caractere reprezentate pe 16 biți în format Unicode (standard internațional de codare a caracterelor care este capabil să reprezinte caracterele tuturor limbilor scrie).
Cu toate că noile clase Reader/Writer dublează clasele InputStream/OutputStream, acestea din urmă nu au fost abandonate, datorită în principal cerinței de compatibilitate cu aplicațiile dezvoltate într-o versiune mai veche de Java. Totuși, există unele metode din vechile clase care se recomandă a nu a mai fi folosite, fiind caracterizare ca depășite (deprecated). Funcțiile oferite de clasele InputStream/OutputStream se regăsesc în clasele Reader/Writer cu mici deosebiri, acest lucru reflectându-se și în denumirile asemănătoare ale celor două tipuri de clase de tip flux, prefixele fiind aceleași. În cazul claselor de citire a datelor se pot observa următoarele corespondențe:
- InputStream - Reader
- ByteArrayInputStream – CharArrayReader
- StringBufferInputStream – StringReader
- FileInputStream – FileReader
- Există unele clase, SequenceInputStream, și DataInputStream care nu au o versiune de tip Reader.
În cazul claselor pentru scriere, se pot observa următoarele corespondențe:
- OutputStream - Writer
- ByteArrayOutputStream – CharArrayWriter
- FileOutputStream – FileWriter