|
CS 596 Java Programming
Fall Semester, 1998
Java IO part 1
|
|
|
To Lecture Notes Index
© 1998, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 21-Dec-98
|
|
Contents of Doc 15, Java IO part 1
References
The
Java Programming Language
,
2
nd
Ed., Arnold and Gosling, Chapter 12
On-line
Java Documentation
Java
source code
From
a programmer's point of view, the user is a peripheral that types when you
issue a read request
- Peter
Williams
Java
I/O Introduction
Java
I/O is defined in terms of streams
Streams
are ordered sequences of data that have:
- A
source (input streams) or
- A
destination (output stream)
Streams
as "Pipes"
- InputStream
-
- OutputStream
Streams
are designed to be combined
The
hardest part for beginners is to understand how to combine Streams. You need to
realize is that each stream does one thing. This leads to a long list of
streams, which can be overwhelming.
The
Four Fold Way
There
is some symmetry in the different streams. This makes it easier to deal with
the large number of different Streams
Byte
based streams
- Streams
uses to read/write raw bytes
- Initially
used to read/write text
- Do
not handle Unicode well
- java.io
classes ending in Stream
Character
based streams
- Use
for all text IO
- Handles
Unicode and local encodings properly
- More
efficient than byte based streams
- java.io
classes ending in Reader or Writer
Input
Streams
- Used
for input - reading
- Byte
based input stream classes end in InputStream
- Character
based input stream classes end in Reader
Output
Streams
- Used
for output - writing
- Byte
based input stream classes end in OutputStream
- Character
based input stream classes end in Writer
The
Four Fold Way - The Streams
Byte
Based
|
Input
|
Output
|
BufferedInputStream
|
BufferedOutputStream
|
ByteArrayInputStream
|
ByteArrayOutputStream
|
DataInputStream
|
DataOutputStream
|
FileInputStream
|
FileOutputStream
|
FilterInputStream
|
FilterOutputStream
|
InputStream
|
OutputStream
|
LineNumberInputStream
|
|
ObjectInputStream
|
ObjectOutputStream
|
PipedInputStream
|
PipedOutputStream
|
|
PrintStream
|
PushbackInputStream
|
|
SequenceInputStream
|
|
StringBufferInputStream
|
|
Character
Based
|
Input
|
Output
|
BufferedReader
|
BufferedWriter
|
CharArrayReader
|
CharArrayWriter
|
FileReader
|
FileWriter
|
FilterReader
|
FilterWriter
|
InputStreamReader
|
OutputStreamWriter
|
LineNumberReader
|
|
PipedReader
|
PipedWriter
|
|
PrintWriter
|
PushbackReader
|
|
Reader
|
Writer
|
StringReader
|
StringWriter
|
Stream
Functionality
Each
type of stream results in four actual streams:
- Byte
based input
- Byte
based output
- Character
based input (Reader)
- Character
based output (Writer)
Piped
Streams
- Useful
in communications between threads
ByteArray
Streams
- Read/write
byte arrays
Filter
Streams
- Abstract
classes that filter bytes from another stream
- Can
be chained together
Buffered
Streams
- Adds
buffering
Data
Streams
- Reads/writes
built-in types (int, float, etc)
- Steam
stores types in binary not ASCII or Unicode
File
Streams
- Used
for file IO
Readers/Writers
Reader
An
abstract class representing an input stream of characters. All Readers are
based on this class. Note the low level of operations. Readers read a character
at a time.
Methods
close() | read(char[], int, int) |
mark(int) | ready() |
markSupported() | reset() |
read() | skip(long) |
read( char[] ) | |
Subclasses
BufferedReader
|
FilterReader
|
PipedReader
|
CharArrayReader
|
InputStreamReader
|
PushbackReader
|
FileReader
|
LineNumberReader
|
StringReader
|
Description
of Methods for Reader
close()
- Close
the stream.
mark(int)
- Mark
the present position in the stream.
markSupported()
- Tell
whether this stream supports the mark() operation.
read()
- Read
a single character.
read(char[])
- Read
characters into an array.
read(char[],
int, int)
- Read
characters into a portion of an array.
ready()
- Tell
whether this stream is ready to be read.
reset()
- Repositions
the stream to the last marked position.
skip(long)
- Skip
n characters.
Reader
Example
A
simple example of using a Reader. In this case, it is a FileReader.
class CountSize
{
public static void main( String args[] ) throws IOException,
FileNotFoundException
{
try
{
Reader in = new FileReader( "Exam" );
int total;
for ( total = 0; in.read() != -1; total++)
;
System.out.println( total );
}
catch ( FileNotFoundException fileProblem )
{
System.err.println( "File not found" );
throw fileProblem;
}
catch ( IOException ioProblem )
{
System.err.println( "IO problem" );
throw ioProblem;
}
}
}
Output
7428
Read,
int and Char
read()
returns -1 if end of stream is reached, else returns the next byte. The byte
must be caste to a char in order to be treated as character, rather than an
integer type.
class Read
{
public static void main( String args[] ) throws IOException
{
Reader in = new StringReader( "abcdefghij" );
System.out.println( in.read() + " raw read, ascii value " );
System.out.println( (char) in.read() + " cast read to char " );
}
}
Output
97 raw read, ascii value
b cast read to char
Writer
Abstract
class representing an output stream of chars. All Writers are based on this
class. This is the output equivalent of Reader.
Methods
close() | write(char[], int, int) |
flush() | write(int) |
write(char[]) | write(String) |
write(String, int, int) |
Description
of Methods
close()
- Closes
the stream
flush()
- Flushes
the stream
write(char[])
- Writes
an array of chars
write(chars[],
int, int)
- Writes
a sub array of chars
write(int)
- Writes
a single character
write(String)
- Write
a string
FileWriter
Example
A
simple example of using a FileWriter.
class Capitalize
{
public static void main( String args[] ) throws IOException
{
Reader in = new StringReader( "abcdefghij" );
Writer out = new FileWriter( "Example");
int nextChar;
while ( ( nextChar = in.read() ) != -1 )
out.write( Character.toUpperCase( (char) nextChar ) );
out.write( '\n' );
out.flush(); // not needed before a close
out.close(); // exiting program closes the Writer
}
}
Output
ABCDEFGHIJ
PrintWriter
PrintWriter
contains higher level output methods to write data. Print() and println() send
the toString() method to objects.
Constructors
PrintWriter(Writer)
- Creates
a new PrintWriter.
PrintWriter(Writer,
boolean)
- Creates
a new PrintWriter, with auto flushing.
Some
PrintWriter Methods
checkError() | print(int) | println(double) |
close() | print(long) | println(float) |
flush() | print(Object) | println(int) |
print(boolean) | print(String) | println(long) |
print(char) | println() | println(Object) |
print(char[]) | println(boolean) | println(String) |
print(double) | println(char) | setError() |
print(float) | println(char[]) | write(int) |
checkError()
- Flushes
the print writer and returns whether or not there was an error on the writer.
PrintWriter
Example
An
example of combining writers. Each writer has two "ends": the writing end, and
the output end. The output end of the writer is connected to an output source
when the writer is created. The FileWriter, out, has its output end connected
to the file named "LetterHome". The statement "new PrintWriter( out );"
connects the output end of the PrintWriter, easyOut, to the writing end "out".
When we write to easyOut, the result of that operation is sent to the writing
end of the writer "out". This then sends the result to the file "LetterHome".
It is very important that you understand how to connect writers together.
class EasyPrint
{
public static void main( String args[] ) throws IOException
{
Writer out = new FileWriter( "LetterHome");
PrintWriter easyOut = new PrintWriter( out );
easyOut.println( "Hi Mom" );
easyOut.println( 5 );
easyOut.println( true );
easyOut.close();
}
}
Output
File
Hi Mom
5
true
In
Slow Motion
Some
visuals to help explain combing writers.
Writer out = new FileWriter( "LetterHome");
PrintWriter easyOut = new PrintWriter( out );
Buffering
IO
Readers
and Writers are not buffered. If you want them to be buffered you need to
explicitly buffer them. BufferedWriter has a constructor that allows you to set
the buffer size. The underlying OS may or may not buffer IO. This example also
illustrates the combining of three writers. Please make sure you understand how
it works.
Buffer
any IO operations that might be expensive
class BufferExample
{
public static void main( String args[] ) throws IOException
{
Writer out = new FileWriter( "LetterHome");
BufferedWriter buffered = new BufferedWriter( out);
PrintWriter easyOut = new PrintWriter( buffered );
easyOut.println( "Hi Mom and Dad" );
easyOut.println( "Please send " );
easyOut.println( 5.03 );
easyOut.println( "dollars");
easyOut.println( true );
easyOut.close();
}
}
In
Slow Motion
Writer out = new FileWriter( "LetterHome");
BufferedWriter buffered = new BufferedWriter( out);
PrintWriter easyOut = new PrintWriter( buffered );
Shorthand
Notation
Chaining
streams together becomes tiresome after a while. Here is a shorter way to
combine streams/readers/writers.
class Test
{
public static void main( String args[] ) throws IOException
{
PrintWriter easyOut =
new PrintWriter(
new BufferedWriter(
new FileWriter( "Example")
)
);
easyOut.println( " Hi Mom" );
easyOut.println( 5 );
easyOut.println( true );
easyOut.close();
}
}
PrintStream
Used
for "ASCII" output
System.out
is a PrintStream
PrintWriter
is preferred over PrintStream since PrintWriter handles Unicode better.
System.out is a PrintStream for several reasons. First, PrintWriter did not
exist in JDK 1.0.x, so System.out started as a PrintStream. Second, Sysem.out
is connected to an ASCII display device, so Unicode characters can not be
displayed. To display Unicode characters one needs the use of fonts, which
requires using AWT in Java. Third, System.out is not meant to be the primary
output mechanism in Java. Programs in Java are meant to use GUI interfaces
using AWT components.
class PrintStreamExample{
public static void main( String args[] ){
System.out.println( "Hi Mom" );
}
}
Constructors
Constructors
were deprecated in JDK 1.1, to discourage use of PrintStream. JDK 1.2
undeprecates them.
PrintStream
Methods
checkError() | print(int) | println(double) |
close() | print(long) | println(float) |
flush() | print(Object) | println(int) |
print(boolean) | print(String) | println(long) |
print(char) | println() | println(Object) |
print(char[]) | println(boolean) | println(String) |
print(double) | println(char) | write(byte[], int, int) |
print(float) | println(char[]) | write(int) |
checkError()
- Flushes
the print stream and returns whether or not there was an error on the output
stream.
PrintStream,
'\n' and auto flush
If
auto flushing is on (default) then printing a '\n' flushes the buffer in a
PrintStream.
The
PrintStream should look for the platform's newline character(s) rather than the
'\n', but does not. It does use the platform's newline character when you use
println();
What
is a newline?
Different
platforms (Unix, PC, and Mac) use different characters to represent a newline.
PrintStream, PrintWriter use platform specific newline. BufferedWriter has a
newLine() method which uses platform specific newline.
System.getProperty(
"line.separator") will return the line separator or newline character(s) for
the platform the program is running on.
The
toString() Standard
We
are now in a position to remove the magic of the toString() standard. First,
note the behavior difference between printing a PoorPrint object and a
FancyPrint object.
class PoorPrint
{
String name = "sam";
}
class FancyPrint
{
String name = "pete";
public String toString()
{
return "FancyPrint( " + name + " )";
}
}
class TestPrint
{
public static void main( String args[] )
{
PoorPrint outOfIt = new PoorPrint();
FancyPrint cool = new FancyPrint();
System.out.println( outOfIt );
System.out.println( cool );
}
}
Output
PoorPrint@5e300848
FancyPrint( pete )
The
toString() StandardHow Does it Work?
The
three methods below show how the toString() works when printing objects. The
print() method calls a method in the String class, which sends the toString()
method to the object. If the class does not implement the toString() method,
the one in the Object class is used. The toString() method in object prints the
name of the object's class and the object's hashCode, which currently is its
location in memory.
Print
in PrintStream
public void print( Object obj ) {
write( String.valueOf( obj ) );
}
String.valueOf
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
Object.toString()
public String toString() {
return getClass().getName() + "@" +
Integer.toHexString(hashCode());
}
Easy
Input - UnicodeReader (ASCIIInputStream)
It
is hard to explain why Java does not have a Reader or InputStream to perform
high level IO. Nevertheless, they do not exist in Java's API. The
sdsu.io.UnicodeReader and sdsu.io.ASCIIInputStream provide some high level IO
methods.
Constructors
UnicodeReader
(Reader)
- Creates
a new UnicodeReader.
Unicode
Methods
eof() | readDouble() | readLong() |
flushLine() | readFloat() | readShort() |
read(char[]) | readFully(char[]) | readUnsignedByte() |
read(char[], int, int) | readFully(char[], int, int) | readUnsignedShort() |
readBoolean() | readInt() | readWord() |
readChar() | readLine() | skipChars(int) |
Using
UnicodeReader
import java.io.*;
import sdsu.io.*;
class ReadingFileExample
{
public static void main( String args[] ) throws Exception
{
FileReader inputFile;
BufferedReader bufferedFile;
UnicodeReader cin;
inputFile = new FileReader( "ReadingFileExample.java" );
bufferedFile = new BufferedReader( inputFile );
cin = new UnicodeReader( bufferedFile );
System.out.println( cin.readWord() );
for ( int k = 1 ; k < 4; k++ )
System.out.println( cin.readLine() );
}
}
Output
import
java.io.*;
import sdsu.io.*;
class ReadingFileExample
Reading/Writing
Binary Data
DataOutputStream
and DataInputStream write and read binary data. Using DataOutputStream's
writeFloat() method will write a float in binary format. Beginners often try to
read primitive Java types from an ASCII file using DataInputStreams. This does
not work. If you need to read/write primitive types to a file in ASCII format,
use UnicodeReader or ASCIIOutputStream. If you want to read/write data in
binary format use DataOutputStream and DataInputStream.
Unicode
IO
Writers
and OutputStreams do not write Unicode strings to a file or to the screen. They
both convert to Unicode strings to ASCII strings. The method used to convert
differs between Writers and OutputStreams.
OutputStreams
just drop the high-order byte. This works only if the original string contains
only ASCII characters. Thus, OutputStreams and InputStreams do not properly
handle any characters outside of the ASCII range in any country. This is why
OutputStreams and InputStreams should not be used for text IO.
Writers
use special encodings to translate Unicode characters to ASCII characters.
Since there are 38,887 Unicode characters and 255 ASCII characters, only 255
Unicode characters can be properly converted. However, in many cases a language
does not need to use more than 255 Unicode characters. Different encodings
exist for different languages to handle the different Unicode characters needed
in the language. For example the 8859_5 encoding is the ISO Latin/Cyrillic
encoding. The Unicode range for the Cyrillic alphabet is \u0400-\u04FF, which
is 255 characters. In Russia, the default encoding for Writers & Readers
might be 8859_5. Thus, Russian users of Java programs can store Cyrillic
characters in files. The following program on the next slide prints 16 Cyrillic
characters to a file. It then reads the file and compares them to the Unicode
characters.
There
are several ways to write Unicode strings to a file and not lose any Unicode
information. First, use the UTF8 encoding in OutputStreamWriter and
InputStreamReader. UTF8 uses a variable length byte-encoding scheme that maps
ASCII characters to one byte and other Unicode characters to multi-byte
representations. Second, use DataOutputStream.writeUTF() and
DataInputStream.readUTF() methods. These two methods use the UTF8 encoding
without you having to specify it. Third, use ObjectInputStream and
ObjectOutputStream. The first two ways are demonstrated on slides 26 & 27.
Specifying
the Unicode Encoding
public class Cyrillic
public static void main( String args[] ) throws IOException
{
//Output streams
FileOutputStream fileOut =
new FileOutputStream( "UnicodeTest");
BufferedOutputStream bufferedOut =
new BufferedOutputStream( fileOut);
PrintWriter out =
new PrintWriter(
new OutputStreamWriter( bufferedOut, "MacCyrillic" ));
//Cyrillic is \u0400 to \u04FF, prints some out
for ( char k = 0x0410; k < 0x0420; k++)
out.print( k );
out.close();
// Input streams
FileInputStream fileIn = new FileInputStream( "UnicodeTest" );
BufferedInputStream bufferedIn =
new BufferedInputStream( fileIn );
InputStreamReader in =
new InputStreamReader( bufferedIn, "MacCyrillic" );
char firstCyrillicChar = 0x0410;
for ( char index = 0; index < 0x0010; index++)
{
char input = (char) in.read();
if ( input == (firstCyrillicChar + index ))
System.out.println( "OK on char " + (int) index);
else
System.out.println( "Bad news " + (int) index);
}
}
}
UTF8
with Readers/Writers
This
example shows using UTF8 encoding. To show that the encoding works some Unicode
characters are saved in a file and read back in.
public class UTF8
{
public static void main( String args[] ) throws IOException
{
FileOutputStream fileOut = new FileOutputStream( "UnicodeTest");
BufferedOutputStream bufferedOut = new BufferedOutputStream( fileOut);
PrintWriter out =
new PrintWriter( new OutputStreamWriter( bufferedOut, "UTF8" ));
for ( char k = 0x0E01; k < 0x0E7F; k =(char) (k + 0x0005))
out.print( k );
out.close();
FileInputStream fileIn = new FileInputStream( "UnicodeTest" );
BufferedInputStream bufferedIn = new BufferedInputStream( fileIn );
InputStreamReader in = new InputStreamReader( bufferedIn, "UTF8" );
char firstThaiChar = 0x0E01;
for ( char index = 0; index < 0x007F; index =(char) (index + 0x0005))
{
char input = (char) in.read();
if ( input == (firstCyrillicChar + index ))
System.out.println( "OK on char " + (int) index);
else
System.out.println( "Bad news " + (int) index);
}
}
}
DataOutputStream/InputStream
& Unicode
This
example shows writing Unicode strings using DataOutputStream/InputStreams.
Recall that DataOutputStream/InputStream write all data ( Strings, ints,
floats, etc. ) in binary. Using InputStreamReader and OutputStreamWriter one
can read/write ints, floats, etc in ASCII. (One can not do this directly, but
it can be done).
public class UnicodeIO
{
public static void UnicodeFile() throws IOException
{
FileOutputStream fileOut = new FileOutputStream( "UnicodeTest");
BufferedOutputStream bufferedOut = new BufferedOutputStream( fileOut);
DataOutputStream out = new DataOutputStream( bufferedOut );
out.writeUTF( "\u0E50 is zero in Thai" );
out.close();
FileInputStream fileIn = new FileInputStream( "UnicodeTest" );
BufferedInputStream bufferedIn = new BufferedInputStream( fileIn );
DataInputStream in = new DataInputStream( bufferedIn );
String fileContents = in.readUTF();
if ( fileContents.charAt( 0 ) == '\u0E50' )
System.out.println( "Can write/read unicode");
}
}
Creating
New Streams
UpperCaseReader
Example
This
slide and the next contain an example of creating a new Reader class.
public static void main( String args[] ) throws Exception
{
Reader input = new StringReader( "hi mom how is dad?" );
// UpperCaseReader is define on next slide
input = new UpperCaseReader( input );
UnicodeReader cin = new UnicodeReader( input );
System.out.println( cin.readWord() );
System.out.println( cin.readLine() );
}
Output
HI
MOM HOW IS DAD?
UpperCaseReader
Example - Continued
Extending
from FilterReader reduces the number of methods we need to implement. The
abstract class FilterReader contains some methods that we do not have to
implement. You can extend the Reader class, but then will have to implement the
methods that FilterReader does for you.
class UpperCaseReader extends FilterReader {
public static final int EOF = -1;
public UpperCaseReader( Reader input ) {
super( input );
}
public int read() throws IOException {
int nextChar = super.read();
if ( nextChar == EOF )
return EOF;
else
return toUpperCase( (char) nextChar );
}
public int read(char chars[], int offset, int length) throws
IOException {
int charsRead = super.read(chars, offset, length);
if ( charsRead == EOF )
return EOF;
for ( int k = offset; k < offset + charsRead; k++)
chars[ k ] = toUpperCase( chars[ k ] ) ;
return charsRead;
}
private char toUpperCase( char convertMe ) {
return Character.toUpperCase( convertMe );
}
}
Useful
StringWriter
The
StringWriter writes to a string. We can use any Writer with a StringWriter to
write to a string. This can be very useful. The following example shows how to
use the StringWriter. The example uses UpperCaseWriter defined on the next slide.
class StringWriterExample
{
public static void main( String args[] ) throws Exception
{
StringWriter outputString = new StringWriter();
// see next slide for UpperCaseWriter
UpperCaseWriter upperCaseOutput =
new UpperCaseWriter( outputString);
PrintWriter cout = new PrintWriter( upperCaseOutput );
cout.println( "Hi Mom" );
cout.println( 3.14);
System.out.println( outputString.toString() );
}
}
Output
HI MOM
3.14
Example
of a FilterWriter
class UpperCaseWriter extends FilterWriter {
public static final int EOF = -1;
public UpperCaseWriter( Writer output ) {
super( output ); // input is stored in field "out"
}
public void write(int c) throws IOException {
if ( c == EOF)
super.write( c );
else
super.write( Character.toUpperCase( (char) c ));
}
public void write(char cbuf[], int off, int len) throws IOException {
write( new String( cbuf), off, len);
}
public void write(String str, int off, int len) throws IOException {
if ( len <= 0 )
super.write( str, off, len);
else
super.write( str.toUpperCase(), off, len);
}
}
Compressed
Streams
Java
1.1 introduced streams to compress data: GZIPOutputStream and GZIPInputStream.
An example is given below.
java.util.zip.GZIPOutputStream
- compressed
output using gzip format
java.util.zip.GZIPInputStream
- decompressed
gzip data
Sample
Use of GZIP classes
import java.io.*;
import sdsu.io.ASCIIInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.GZIPInputStream;
class Compressed{
public static void main( String args[] ) throws IOException {
OutputStream outputFile =
new FileOutputStream( "Compressed" );
PrintWriter cout =
new PrintWriter (new GZIPOutputStream( outputFile ));
cout.println("Hi Mom");
cout.close();
// the file "Compressed" now contains compress version of
// the string "Hi Mom"
FileInputStream inputFile =
new FileInputStream( "Compressed" );
ASCIIInputStream cin =
new ASCIIInputStream (
new GZIPInputStream( inputFile ));
System.out.println( cin.readLine());
}
}
Input/Output
Streams
input/output
Streams are used in the same way as Readers/Writers. We will go through one
example of using a Stream.
OutputStream
Abstract
class representing an output stream of bytes. All OutputStreams are based on
this class. Note byte oriented IO works on ASCII only, not Unicode
Methods
close() | write(byte[], int, int) |
flush() | write(int) |
write(byte[]) | |
Description
of Methods
close()
- Closes
the stream
flush()
- Flushes
the stream
write(byte[])
- Writes
an array of bytes
write(byte[],
int, int)
- Writes
a sub array of bytes
write(int)
- Writes
a byte
OutputStream
Example
class CountSize
{
public static void main( String args[] ) throws IOException
{
InputStream in;
in = new StringBufferInputStream( "abcdefghij" );
OutputStream out = System.out;
int nextChar;
while ( ( nextChar = in.read() ) != -1 )
out.write( Character.toUpperCase( (char) nextChar ) );
out.write( '\n' );
out.flush();
}
}
Output
ABCDEFGHIJ
Copyright © 1998 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
All rights reserved.
visitors since 19-Oct-98