Hey folks! My name is Julie I am a Java developer at VeePN company. I was one of the developers who made VeePN VPN on Mac OS and Windows OS. I have more than 5 years of experience in the programming sphere. So today I want to share with you how to read from the output stream. Let's start!
The notation I/O stands for input and output. Through I / O, the program interacts with the outside world. Input is read and output is written. On the JavaTM platform, I/O is based on a threading model. This model allows you to read from files, network connections, or consoles in the same way. You do not need to change the program code depending on the type of input device. This is also true for output devices.
The stream-based model works well when reading from an input stream and writing to an output stream. However, there are additional considerations when you need to receive data written to the output stream or write data to the input stream for later reading. This article looks at some of these points.
Input streams are used for reading. The program opens an input stream to read information from the source. Output streams are used for writing. To write information to an output device, a program opens an output stream for that device and then writes the information to that stream.
The java.io package contains various stream classes for reading from a stream and for writing to a stream. These classes can be divided into two categories: character streams and byte streams. The character stream classes are for reading and writing character data. The byte stream classes are for writing and reading binary data (that is, bytes). Reader and Writer are abstract superclasses for byte streams. Subclasses of these superclasses implement streams for specific sources and output devices.
Sometimes the question arises, how to read the information just written to the stream? For example, suppose you write something to the output stream. To do this, you passed an OutputStream or Writer to some library method. How do you now read from the OutputStream or Writer what you have written?
One way to read the written information is to pass one of the file-related output streams to the library method: FileOutputStream or FileWriter. This will give you the opportunity to re-read the data written to the file system. Here is an example:
import java.io.*;
public class FileRead {
public static void main(String args[]) throws IOException {
// create temporary output file
File file = File.createTempFile("zuk", ".tmp");
// delete at the end of the program
file.deleteOnExit();
// create an output stream for the file
Writer writer = new FileWriter(file);
// pass data to the output stream
save(writer);
// close output
writer.close();
// Open output as input
Reader reader = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(reader);
// read input
string line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
bufferedReader.close();
}
private static void save(Writer generic) throws IOException {
PrintWriter out = new PrintWriter(generic);
out.println("Line One");
out.println("Line Two");
}
}
opens the output as an input stream and reads from the input stream. The program should display the lines read again:
Line one
Line Two
This approach is used in cases where the amount of output data is greater than the available memory. In these cases, you must still write the output to an external source.
RAM Usage
If the intermediate information is relatively small in size, you can use stream classes that use RAM. These classes use RAM to read and write data (unlike streams like FileWriter, which uses an external source for these operations). You can use a library that passes information to the appropriate in-memory stream class: for output, this can be either a ByteArrayOutputStream or a StringWriter. Then, to read the information in memory, you can convert this stream to an input stream: ByteArrayInputStream or StringReader.
The ByteArrayOutputStream and StringWriter output streams rely on an internal byte array and a StringBuffer to store intermediate information. During input, ByteArrayInputStream and StringReader work with array and String for input. This means that when you need to change the mode from output to input, you will get the current contents of the output device to create the input device.
In the case of byte streams, ByteArrayOutputStream and ByteArrayInputStream work together. After the end of writing, you get the bytes with the toByteArray() method. Bypassing bytes to the ByteArrayInputStream, you will fill the input stream:
// Create an output stream
ByteArrayOutputStream outputStream =
new ByteArrayOutputStream(initialSize);
// write to stream
...
// after the end get the bytes
byte bytes[] = outputStream.toByteArray();
// create an input stream
ByteArrayInputStream inputStream =
new ByteArrayInputStream(bytes);
// read from stream
...
There are a few things to keep in mind when using ByteArrayOutputStream. First, it's good if you can estimate the size of the output. If you don't specify an initial size, the byte array will be 32 bytes in size and grow by a power of two each time the internal buffer is full (growth is even faster if you write the array instead of a character). If you know there will be at least 2000 characters, start with this size and you will avoid resizing at 64, 128, 256, 512, 1024 and 2048 characters. One more point - the toByteArray() method does not return a reference to the internal byte array. Instead, it returns a copy. This can be both good and bad. The copy prevents the buffer from being modified, but there are two sets of data and it uses twice as much memory.
For character streams, there are StringWriter and StringReader. StringWriter uses an internal StringBuffer to control how characters are read. The code for character streams is similar to the code for byte streams:
// create an output stream
StringWriter writer =
new StringWriter(initialSize);
// write to stream
...
// after the end get the bytes
String string = writer.toString();
// create an input stream
StringReader reader =
new StringReader(string);
// read from stream
...
StringWriter uses a character array for internal storage. The character array is in a StringBuffer. As the array fills up, it grows in size with the same duplication effect described earlier for the ByteArrayOutputStream. If you don't specify an initial size, the StringBuffer array will start with just 16 characters. As always, specify the most realistic size of the array. Note that it is possible to retrieve the contents of the StringBuffer used by the StringWriter class without allocating additional memory. For more information about this, see the documentation for the toString method of the StringBuffer class.
Using the StringWriter-StringReader pair allows you to modify the example above and perform all memory accesses:
import java.io.*;
public class MemRead {
public static void main(String args[]) throws IOException {
// create an output stream in memory
StringWriter writer = new StringWriter(128);
// pass data to the output stream
save(writer);
// close output
writer.close();
// open output as input
Reader reader = new StringReader(writer.toString());
BufferedReader bufferedReader = new BufferedReader(reader);
// read input
string line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
bufferedReader.close();
}
private static void save(Writer generic) throws IOException {
PrintWriter out = new PrintWriter(generic);
out.println("Line One");
out.println("Line Two");
}
}
Using Filters While Reading
There is at least one other way to read from the output stream. This method uses filters. Both the byte and character stream classes allow filters to be set on I/O streams. By leaving the basic read and write operations unchanged, filters enrich streams with new features. The BufferedReader class used in previous programs is an example of a filter. It processes the input data in an internal buffer and passes characters to the reader on request. Instead of accessing the data source for each request, BufferedReader reads the input data into memory to improve performance. Since the MemRead program uses a buffer located in RAM, there is no real performance difference. For the FileRead program, there is a performance difference, although for small data it is minimal.
Filters are usually built into the processing sequence as follows: the original source or destination is passed to the constructor, then the filter does its processing before (or after) passing bytes or characters to the original source or destination. A filter subclass can be either an existing class or one of the filter streams. Filter stream classes depend on the type of data being filtered: FilterInputStream, FilterOutputStream, FilterReader, or FilterWriter.
Filters in the program are applied in the same way as it was shown earlier for BufferedReader, that is:
SourceStream source = new SourceStream(...);
AFilterStream filter = new AFilterStream(source);
// use filter
...
// close filter, not source
filter.close();
The following is a program that uses a filter to count characters, numbers, and spaces. When the filter closes, it writes the counters to the stream sent to the constructor:
import java.io.*;
public class CountWriter extends FilterWriter {
PrintStream out;
int chars, nums, whites;
public CountWriter(Writer destination, PrintStream out) {
super(destination);
this.out = out;
}
public void write(int c) throws IOException {
super.write(c);
check((char)c);
}
public void write(char cbuf[], int off, int len) throws IOException {
super.write(cbuf, off, len);
for (int i = off; i < len; i++) {
check(cbuf[i]);
}
}
public void write(String str, int off, int len) throws IOException {
super.write(str, off, len);
for (int i = off; i < len; i++) {
check(str.charAt(i));
}
}
private void check(char ch) {
if (Character.isLetter(ch)) {
char++;
} else if (Character.isDigit(ch)) {
nums++;
} else if (Character.isWhitespace(ch)) {
whites++;
}
}
public void close() {
out.println("Chars: " + chars);
out.println("Nums: " + nums);
out.println("Whitespace: " + whites);
}
}
An Example of Applying Filters Let's modify the above program to use a filter. Note that you don't need to reread the data to perform the required output:
import java.io.*;
public class MemRead2 {
public static void main(String args[]) throws IOException {
// create an output stream in memory
StringWriter writer = new StringWriter(128);
CountWriter counter = new CountWriter(writer, System.err);
// pass data to the output stream
save(counter);
// close output stream
counter.close();
}
private static void save(Writer generic) throws IOException {
PrintWriter out = new PrintWriter(generic);
out.println("Line One");
out.println("Line Two");
}
}
After executing the program, you should get the following output:
Chars: 14
Numbers: 0
Whitespace: 4
That's basically all there is to read from output streams. You can either use the direct approach of reading the fully written output, or you can intercept the output while it is being written to perform your read operation.
Built With
- fileoutputstream
- filewriter
- i/o
- java
- javatm
- outputstream