Input/Output Streams

De la WikiLabs
Versiunea din 15 decembrie 2013 11:59, autor: Rhobincu (discuție | contribuții) (→‎Fluxuri de intrare-ieșire la nivel de caracter)
(dif) ← Versiunea anterioară | Versiunea curentă (dif) | Versiunea următoare → (dif)
Jump to navigationJump to search

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

Graphical representation of a generic InputStream and a FileInputStream

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:

Another category of classes derived from InputStream, this time though not directly, are filter classes, derived from FilterInputStream which is, in its turn, derived from InputStream. These types of classes do not offer support for any additional data sources but instead they work as a wrapper over exiting input streams, providing additional functionality. Examples of such classes are:

  • DataInputStream is one of the most used filter classes. In contains several methods that can read primitive data types (int, float, etc.), in a system independent way.
  • PushbackInputStream offers the possibility of rewind and read data that has already been read.

Next, an example of using class 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();

By using class ByteArrayInputStream, you can read data from an array using stream reading methods, without having to manually manage the current read index in the array. The connection is easily done by giving the existing array as an argument to the stream constructor. The example above creates a 10 bytes array, filling it with value from 0 to 9 and then creating a ByteArrayInputStream and using it to sequentially read the data, byte by byte and displaying it on the screen. You can also see that the reading is only done until the read() method returns -1, signaling the end of the stream, thus the end of the array.

Byte Output Streams

Graphical representation of a generic OutputStream and a FileOutputStream

For writing data using streams, a class hierarchy is used, derived from class java.io.OutputStream]. Just like InputStream, class OutputStream is abstract, offering an interface for declaring generic write methods. The class has the following definition:

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;
}

Method write() writes the lest significant bytes of the argument, which is of type int to the stream. The other methods write the contents of a byte array b or a section of it, starting from position off and having a length of len.

Method flush() forces writing data to the stream, and it is useful when a write buffer is used.

Method close() closes the stream and frees and resource associated with it.

Just like InputStream, classes derived from OutputStream can be split in two categories: data streams (ByteArrayOutputStream, FileOutputStream, etc.) which connect to a destination, and filter streams (derived from FilterOutputStream) which are wrappers over the first ones, enhancing their functionality.

Standard output stream (System.out) and standard error streams (System.err) are static fields of class System, of type PrintStream, which extends class FilterOutputStream. These are connected to the display and by conventions are used in two distinct situations: first to display messages for the program user, and the second, to display error messages (usually for debugging purposes).

Next, let's see an example of using a ByteArrayOutputStream, used to display all 8-bits values:

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());
}

Class ByteArrayOutputStream corresponds to ByteArrayInputStream and allows the connection of a stream to a buffer (a byte array). The buffer is created when an object of type ByteArrayOutputStream is instantiated by using one of the two constructors. This class offers a method of storing data in memory before processing or sending them somewhere else. In order to transmit data, method writeTo() is used. In the example above, the buffer is written to the out stream, passed as a parameter. Opposite to class ByteArrayInputStream, where the buffer was passed as an argument to the constructor, in the case of ByteArrayOutputStream, in order to obtain a reference to the buffer, a call to toByteArray() is required. This method returns a copy of the buffer.

Character Input/Output Streams

Classes for character streams are extended from class java.io.Reader for reading data and http://docs.oracle.com/javase/7/docs/api/java/io/Writer.html java.io.Writer] for writing data. Most of these classes overlap with byte stream classes, having the same functionality. The difference between them is that InputStream/OutputStream classes work (read/ write) with bytes (8 bits), as Reader/Writer work with characters represented in the Unicode standard, on 16 bits.

The corespondence between bytes streams and character streams are as follow:

In case of input streams:

  • InputStream - Reader
  • ByteArrayInputStreamCharArrayReader
  • StringBufferInputStreamStringReader
  • FileInputStreamFileReader
  • There are classes, like SequenceInputStream and DataInputStream that don't have a Reader version.

In case of output streams: