The IO Package
Contents
Streams Overview
Byte Streams
Character Streams
InputStreamReader and OutputStreamWriter
The Data Byte Streams
The I/O Package
The java.io package defines I/O in terms of streams.
The Java.nio package and its subpackages define I/O in terms of buffers
and channels. Here the “nio” is acronym of new I/O.
Streams
Stream
: an object that either delivers data to its destination (screen,
file, etc.) or that takes data from a source (keyboard, file, etc.)
it acts as a buffer between the data source and destination
Input stream
: a stream that provides input to a program
System.in
is an input stream
Output stream
: a stream that accepts output from a program
System.out
is an output stream
A stream connects a program to an I/O object
The I/O Package
The java.io package defines I/O in terms of streams.
The Java.nio package and its subpackages define I/O in terms of buffers
and channels. Here the “nio” is acronym of new I/O.
Binary Versus Text Files
All
data and programs are ultimately just zeros and ones
each digit can have one of two values, hence
binary
bit
is one binary digit
byte
is a group of eight bits
Text files
: the bits represent printable characters
one byte per character for ASCII, the most common code
for example, Java source files are text files
so is any file created with a "text editor"
Binary files
: the bits represent other types of encoded information,
such as executable instructions or numeric data
these files are easily read by the computer but not humans
they are
not
"printable" files
Java: Text Versus Binary Files
Text files are more readable by humans
Binary files are more efficient
computers read and write binary files more easily than text
Java binary files are portable
they can be used by Java on different machines
Reading and writing binary files is normally done by a program
text files are used only to communicate with humans
Java Text Files
• Source files
• Occasionally input files
• Occasionally output files
Java Binary Files
• Executable files (created
by compiling source files)
• Usually input files
Text Files vs. Binary Files
Number: 127 (decimal)
Text file
―
Three bytes: “1”, “2”, “7”
―
ASCII (decimal): 49, 50, 55
―
ASCII (octal): 61, 62, 67
―
ASCII (binary): 00110001, 00110010, 00110111
Binary file
:
―
One byte (
byte
)
:
01111110
―
Two bytes (
short
): 00000000 01111110
Text file: an example
127 smiley
faces
0000000 061 062 067 011 163 155 151 154
1 2 7 \t s m i l
0000010 145 171 012 146 141 143 145 163
e y \n f a c e s
0000020 012
Binary file: an example
[a .class file]
0000000 312 376 272 276 000 000 000 061
312 376 272 276 \0 \0 \0 1
0000010 000 164 012 000 051 000 062 007
\0 t \n \0 ) \0 2 \a
0000020 000 063 007 000 064 010 000 065
\0 3 \a \0 4 \b \0 5
0000030 012 000 003 000 066 012 000 002
\n \0 003 \0 6 \n \0 002
...
Text File I/O
Important classes for text file
output
(to the file)
PrintWriter
FileOutputStream [
or
FileWriter]
Important classes for text file
input
(from the file):
BufferedReader
FileReader
FileOutputStream
and
FileReader
take
file names
as
arguments.
PrintWriter
and
BufferedReader
provide
useful methods
for
easier writing and reading.
Usually need a
combination of two classes
To use these classes your program needs a line like the following:
Buffering
Not buffered
: each byte is read/written from/to disk as soon as
possible
“little” delay for each byte
A disk operation per byte---higher overhead
Buffered
: reading/writing in “chunks”
Some delay for some bytes
―
Assume 16-byte buffers
―
Reading: access the first 4 bytes, need to wait for all 16 bytes
are read from disk to memory
―
Writing: save the first 4 bytes, need to wait for all 16 bytes
before writing from memory to disk
Every File Has Two Names
1.
the stream name used by Java
outputStream
in the example
2.
the name used by the operating
system
Text File Output
To open a text file for output: connect a text file to a stream for
writing
PrintWriter outputStream =
new PrintWriter(new FileOutputStream("out.txt"));
Similar to the long way:
FileOutputStream s = new FileOutputStream("out.txt");
PrintWriter outputStream = new PrintWriter(s);
Goal: create a
PrintWriter
object
which uses
FileOutputStream
to open a text file
Output File Streams
PrintWriter
FileOutputStream
Disk
Memory
smileyOutStream smiley.txt
Byte Streams (Binary Streams)
Object
InputStream
FileInputStream
FilterInputStream
BufferedInputStream
FilterOutputStream
FileOutputStream
BufferedOutputStream
DataInputStream
OutputStream
DataOutputStream
Byte Streams
InputStream
AutioInputStream
FileInputStream
ObjectInputStream
SequenceInputStream
ByteArrayInputStream
PipedInputStream
Byte Streams
OutputStream
FileOutputStream
ObjectOutputStream
ByteArrayOutputStream
PipeOutputStream
Byte Streams
import java.io.*;
public class CountBytes {
public static void main(String[] args)
throws IOException
{
InputStream
in;
if (args.length == 0)
in = System.in;
else
in = new FileInputStream(args[0]);
int total = 0;
while (in.read() != -1)
total++;
System.out.println(total + " bytes");
}
}
The abstract class
InputStream
declares
methods to read bytes from a particular
source.
Reads a single byte of data and returns the
byte that was read, as an integer in the range
0 to 255, not -128 to 127(unsigned).
Byte Streams
import java.io.*;
public class TranslateByte {
public static void main(String[] args)
throws IOException
{
byte from = (byte) args[0].charAt(0);
byte to = (byte) args[1].charAt(0);
int b;
while ((b = System.in.read()) != -1)
System.out
.write(b == from ? to : b);
}
}
The abstract class
OutputStream
provides an
abstraction for writing bytes to a destination.
Run:
Java TranslateByte b B
Character Streams
Object
Reader
BufferedReader
InputStreamReader
FileReader
Writer
OutputStreamWriter
PrintWriter
BufferedWriter
FileWriter
………
Character Streams
Reader
BufferedReader
InputStreamReader
StringReader
CharArrayReader
PipedReader
Character Streams
Writer
BufferedWriter
OutputStreamWriter
StringWriter
CharArrayWriter
PipedWriter
FilterWriter
Character Streams
import java.io.*;public class CountSpace {
public static void main(String[] args) throws IOException
{
Reader in;
if (args.length == 0)
in = new InputStreamReader(System.in); else
in = new FileReader(args[0]);
int ch; int total;
int spaces = 0;
for (total = 0; (ch = in.read()) != -1; total++) { if (Character.isWhitespace((char) ch)) spaces++;
}
System.out.println(total + " chars " + spaces + " spaces");
}
The abstract classes for reading and writing
streams of characters are
Reader
and
Writer
.
Run:
Java CountSpace CountSpace.java
Result:
520 characters 172 spaces
The abstract class Reader provides a character
stream analogous to the byte stream
InputStream and the methods of Reader
essentially mirror those of InputStream.
The conversion streams
InputStreamReader
Character Streams
The conversion streams
InputStreamReader
and
OutputStreamWriter
translate
between character and byte streams
using either a specified character set
encoding or the default encoding for
the local system.
public Reader readArabic(String file) throws IOException {
InputStream fileIn = new FileInputStream(file); return new InputStreamReader(fileIn,
"iso-8859-6"); }
The Stream types usually have
input/output pairs, and most have both
byte stream and character stream
variants
Filter streams
Buffered streams
Piped streams
A group of streams, called
in-memory streams:
ByteArray streams
CharArray streams
String streams
I/O Streams that have no
O/I counterpart:
The Print streams
LineNumberReader
SequenceInputStream
Streams that are useful
for building parsers
Pushback streams
Filter Streams
import java.io.*;
public class UppercaseConvertor extends FilterReader {
public UppercaseConvertor(Reader in) { super(in);
}
public int read() throws IOException { int c = super.read();
return (c==-1 ? c :
Character.toUpperCase((char)c)); }
public int read(char[] buf, int offset, int count) throws IOException
{
int nread = super.read(buf, offset, count); int last = offset + nread;
for (int i = offset; i < last; i++)
buf[i] = Character.toUpperCase(buf[i]); return nread;
public static void main(String[] args) throws IOException
{
StringReader src = new StringReader(args[0]); FilterReader f = new UppercaseConvertor(src);
int c;
while ( (c=f.read()) != -1) System.out.print((char)c); System.out.println();
} }
Run:
% java UpperCaseConvertor “no lowercase” Filter streams help to chain streams to
produce composite streams of greater utility. They get their power from the ability to filter-process-what they read or write,
transforming the data in some way. abstract class
Buffered Streams, Piped Streams
import java.io.*;
public class BufferedReaderTest { public static void main(String[] args) throws IOException
{
BufferedReader charStream = new BufferedReader (new
InputStreamReader(System.in));
String data = charStream.readLine(); // Read a line from standard input
System.out.println("Input = " + data); }
}
The Buffered stream classes buffer their data to avoid every read or write going directly to the next stream. These classes are often used in conjunction with File streams.
import java.io.*;
class TextGenerator extends Thread { private Writer out;
public TextGenerator(Writer out) { this.out = out;
}
public void run() { try {
try {
for (char c = 'a'; c <= 'z'; c++) out.write(c);
} finally { out.close(); }
} catch(IOException e) {
getUncaughtExceptionHandler().uncaughtException(this, e);
} } }
public class Pipe {
public static void main(String[] args) throws IOException
{
PipedWriter out = new PipedWriter(); PipedReader in = new PipedReader(out); TextGenerator data = new TextGenerator(out); data.start();
int ch;
while ((ch=in.read()) != -1) System.out.print((char) ch); System.out.println();
}
} Result:
Print Streams, LineNumberReader
The Print streams provide methods
that make it easy to write the values of
primitive types and object to a stream,
in a human-readable text format
print and println method
The call out.print(f) is equivalent to
out.write(String.valueOf(f).getBytes());
LineNumberReader
The LineNumberReader stream keeps
track of line numbers while reading
text.
import java.io.*;
public class FindChar {
public static void main(String[] args) throws IOException
{
if (args.length != 2)
throw new IllegalArgumentException( "need char and file");
int match = args[0].charAt(0);
FileReader fileIn = new FileReader(args[1]); LineNumberReader in = new
LineNumberReader(fileIn);
int ch;
while ((ch = in.read()) != -1) { if (ch == match) {
System.out.println("'" + (char) ch + "' at line " + in.getLineNumber());
return ; }
}
System.out.println((char) match + " not found");
Run:
%java FindChar I FindChar.java
Pushback Streams
A Pushback stream lets you
push back, or “unread”
characters or bytes when you
have read too far. Pushback is
typically useful for breaking input
into tokens.
For example, lexical scanners
often know that a token (such as
an identifier) has ended only
when they have read the first
character that follows it.
import java.io.*;
public class SequenceCount {
public static void main(String[] args) throws IOException
{
PushbackInputStream
in = new PushbackInputStream(System.in); int max = 0; // longest sequence found int maxB = -1; // the byte in that sequence int b; // current byte in input
do { int cnt;
int b1 = in.read();
for (cnt = 1; (b = in.read()) == b1; cnt++) continue;
if (cnt > max) {
max = cnt; // remember length
maxB = b1; // remember which byte value }
in.unread(b); // pushback start of ntext seq } while (b != -1); // until we hit end of input System.out.println(max + " byte of " + maxB); }
}
Run and Result:
% java SequenceCount
1
2345
111
StreamTokenzier
The StreamTokenizer gives simple
tokenization. More general facility for
scanning and converting input text is
provided by the java.util.Scanner class.
Four token type
TT_WORD
TT_NUMBER
TT_EOL
TT_EOF
import java.io.*;
class StreamTokenizerDemo {
public static void main(String args[]) { try {
FileReader fr = new FileReader(args[0]); BufferedReader br = new BufferedReader(fr); StreamTokenizer st = new StreamTokenizer(br); st.ordinaryChar('.');
st.wordChars('\'', '\'');
while(st.nextToken() != StreamTokenizer.TT_EOF) { switch(st.ttype) {
case StreamTokenizer.TT_WORD: System.out.println(st.lineno() + ") " + st.sval);
break;
case StreamTokenizer.TT_NUMBER: System.out.println(st.lineno() + ") " + st.nval);
break; default:
System.out.println(st.lineno() + ") " + (char)st.ttype);
} } fr.close(); }
catch (Exception e) {
System.out.println("Exception: " + e);
Input (tokens.txt
)The price is $23.45. Is that too expensive? (I don’t think so.)
The Data Byte Streams
DataInput and DataOutput
These interfaces define
methods that transmit primitive
types across a stream.
Read / Write methods
Read Write Type
readBoolean writeBoolean boolean
readChar writeChar char
readByte writeByte byte
readShort writeShort short
readInt writeInt int
readLong writeLong long
readFloat writeFloat float
readDouble writeDouble double
readUTF writeUTF String(in
UTF format
)
public static void writeData(double[] data, String file) throws IOException
{
OutputStream fout = new FileOutputStream(file); DataOutputStream out = new
DataOutputStream(fout); out.writeInt(data.length)
for(double d : data) out.writeDouble(d); out.close();
}
public static double[] readData(String file) throws IOException
{
InputStream fin = new FileInputStream(file); DataInputStream in = new DataInputStream(fin); double[] data = new double[in.readInt()];
for (int i = 0; i < data.length; i++) data[i] = in.readDouble();
Working with Files
File Streams and FileDescriptor
File Streams allow you to reat a file
as a stream for input or output.
A FileDescriptor object represents a
system-dependent value that
describes an open file.
The RandomAccessFile class
behaves like a large array of bytes
stored in the file system using the
file pointer.
The File class provides methods to
separate pathnames into
subcomponents and to ask the file
system about the file a path name
refers to (You can refer to the other
reference).
FilenameFilter and FileFilter
The FilenameFilter interface
provides objects that filter unwanted
files from a list.
import java.io.*;
public class DirFilter implements
FilenameFilter {
public boolean accept(File dir, String name) {
return new File(dir, name).isDirectory();
}
public static void main(String[] args)
{
File dir = new File(args[0]);
String[] files = dir.list(new DirFilter());
System.out.println(files.length + " dir(s):");
for (String file : files)
System.out.println("\t" + file);
}
Object Serialization
What is Object Serialization?
Serialization: process of converting an object’s
representation into a stream of bytes
Deserialization: reconstituting an object from a
byte stream
Process of reading and writing objects
Writing an object is to represent its state in a
serialized form sufficient to reconstruct the
object as it is read.
Object serialization is essential to building all
Serializing Objects
How to Write to an
ObjectOutputStream
Writing objects to a stream
is a straight-forward
process. Example of
constructing a Date object
and then serializing that
object:
FileOutputStream out = new
FileOutputStream("theTime");
ObjectOutputStream s = new
ObjectOutputStream(out);
s.writeObject("Today");
s.writeObject(new Date());
How to Read from an
ObjectOutputStream
Example that reads in the
String and the Date object
that was written to the file
named theTime in the read
example:
FileInputStream in = new
FileInputStream("theTime");
ObjectInputStream s = new
ObjectInputStream(in);
String today =
(String)s.readObject();
Date date =
Serializing Objects
Providing Object Serialization for Your Classes
Implementing the Serializable Interface
Customizing Serialization
Implementing the Externalizable Interface
Protecting Sensitive Information
[ObjectFileTest.java]
The Java New I/O
The Java New I/O
The new I/O (NIO) APIs introduced in v 1.4 provide new features
and improved performance in the areas of buffer management,
scalable network and file I/O, character-set support, and
regular-expression matching. The NIO APIs supplement the I/O facilities
in the java.io package.
Features
Buffers for data of primitive types
Character-set encoders and decoders
A pattern-matching facility based on Perl-style regular
expressions
Channels, a new primitive I/O abstraction
The Java New File I/O
For the New File I/O : Three Kinds of Objects are Involved
A file stream object : FileOutputStream objects, FileInputStream objects
One or more buffer objects : ByteBuffer, CharBuffer, LongBuffer, etc
A channel object : FileChannel,…
File Stream Object
Buffer Objects
Channel Object The channel transfers data between
Accessing Files
Channels
Channels were introduced in the 1.4 release of Java to provide a faster capability
for a faster capability for input and output operations with files, network sockets,
and piped I/O operations between programs than the methods provided by the
stream classes.
The channel mechanism can take advantage of buffering and other capabilities
of the underlying operating system and therefore is considerably more efficient
than using the operations provided directly within the file stream classes.
A summary of the essential role of each of them in file operations
A File object encapsulates a path to a file or a directory, and such an object
encapsulating a file path can be used to construct a file stream object.
A FileInputStream object encapsulates a file that can be read by a channel. A
FileoutputStream object encapsulates a file that can be written by a channel.
A buffer just holds data in memory. The loaded data to be written to a file will be
saved at buffer using the buffer’s put() method, and retrieved using buffer’s get()
methods.
A FileChannel object can be obtained from a file stream object or a
Accessing Files
Accessing Files
New I/O Example (ReadPrimes)
import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.FileNotFoundException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel;public class ReadPrimes {
public static void main(String[] args) { File aFile = new File("primes.bin"); FileInputStream inFile = null;
try {
inFile = new FileInputStream(aFile);
} catch(FileNotFoundException e) { e.printStackTrace(System.err); System.exit(1);
}
FileChannel inChannel = inFile.getChannel(); final int PRIMECOUNT = 6;
ByteBuffer buf =
ByteBuffer.allocate(8*PRIMECOUNT); long[] primes = new long[PRIMECOUNT];
try {
while(inChannel.read(buf) != -1) { ((ByteBuffer)
(buf.flip())).asLongBuffer().get(primes);
// List the primes read on the same line System.out.println();
for(long prime : primes)
System.out.printf("%10d", prime);
buf.clear(); // Clear the buffer for the next read
}
System.out.println("\nEOF reached.");
inFile.close(); // Close the file and the channel
} catch(IOException e) {
e.printStackTrace(System.err); System.exit(1);
}
System.exit(0); }
} You also need to read the “PrimesToFile.java”