You are on page 1of 52

CSE/IT 213 - File I/O

New Mexico Tech


September 30, 2011

Writing javadoc comments


javadoc will create standard html documentation of
your code
javadoc comments are place in between /** ... */
comment blocks. Its the extra * that sets this as a
javadoc comment rather than a regular comment.
Also use tags set off by the character @ inside javadoc
comments to format the html
For instance variables

/**
* holds the first coordinate of a point (x,y)
*/
private double x;

For methods use tag markers


/**
* sets the x-value of a point
* @param x the first coordinate of the point
* @return void
*/
public void setX(double x) {.... }

Use Eclipse to generate javadocs. The Menu Option is


Project > Generate Javadoc

Input and Output Streams


An input stream is something you read a sequence of
bytes from.
An output stream is something you write a sequence of
bytes to.
Streams dont have to be files. They can be network
connections or blocks of memory.
The classes that are at the top of the class hierarchy
for stream processing are InputStream or OutputStream
2

InputStream
An abstract class. One method it defines is
public abstract int read() throws IOException
Classes that extend InputStream implement this method.
Return -1 if end of input is reached.
For instance, FileInputStream extends InputStream and
implements the read method. Reading one byte at time
from a file.
System.in is a field in System defined as public static
final InputStream in. This provides input from the
stdin.
3

OutputStream
Like InputStream, OutputStream is an abstract class.
One method it expects it subclasses to implement is
abstract void write(int b), which writes one byte at a
time.

When you are done reading or writing a stream, you


need to close it. Closing an output stream, flushes out
the stream, writing out any remaining characters. For
example, the buffer might have been waiting to write
until it received enough input. Closing guarantees the
buffer is written. Otherwise, if you never close a file
the buffer may never be flushed.
Closing a file also frees up system resources.
InputStream and OutputStream are not of much use to
application programmers as they more often deal with
strings and numbers rather than bytes.
Unlike C which gives you only FILE to deal with every
type of file, Java has over 60 different stream types.
5

Different hierarchies for classes that process bytes and


characters. Characters in Java are unicode, not just
ASCII.
DataInputStream and DataOutputStream read and write all
the primitive Java types in binary format.
There are higher level streams to process Audio, Images, and particular file formats, like ZipInputStream and
ZipOutputStream that let you read and write files in the
ZIP compression format.

Low level I/O


FileOutputStream outputs sequence of bytes.
Two ways to open a FlileInputStream
public FileInputStream(File file)
throws FileNotFoundException
public FileInputStream(String name)
throws FileNotFoundException
To use FileOutputStream(File file) have to first create
a File object. File is the physical object, Stream is the
means by which the file will perform input and output.
6

Streams can be associated with networks, serial ports,


etc.
FileInputStream reads from a file a byte or an array of
bytes at a time.
import java.io.*;
public class Junk {
public static void main(String[] args) {
byte [] byteArray = {1,2,3,5,6,7,8,9,10};
File file = new File("test.txt");
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(byteArray);

fos.write(byteArray[2]);
fos.close();
FileInputStream fis = new FileInputStream(file);
int filesize = (int) file.length();
byte[] newByteArray = new byte[filesize];
fis.read(newByteArray);
for (int i = 0; i < filesize; i++)
System.out.print(newByteArray[i] + " ");
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException ioe) {

ioe.printStackTrace();
}
}
}
1 2 3 5 6 7 8 9 10 3
The file created here is a binary file.
$ less test.txt
"test.txt" may be a binary file. See it anyway?
tukey:Junk scott$ hexdump test.txt
0000000 01 02 03 05 06 07 08 09 0a 03
000000a

Can convert bytes to ASCII charactersas they are 8 bits


as well,
import java.io.*;
public class Junk {
public static void main(String[] args) {

byte [] byteArray = {(byte) A, (byte) B, (byte) C, (byte


File file = new File("test.txt");
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(byteArray);
fos.write(byteArray[2]);
7

fos.close();
FileInputStream fis = new FileInputStream(file);
int filesize = (int) file.length();
byte[] newByteArray = new byte[filesize];
fis.read(newByteArray);
for (int i = 0; i < filesize; i++) {
System.out.print((char) newByteArray[i] + " ");
System.out.print(newByteArray[i] + " ");
}
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException ioe) {

ioe.printStackTrace();
}
}
}
A 65 B 66 C 67 a 97 b 98 c 99 C 67
This produces a text file
$ file test.txt
test.txt: ASCII text, with no line terminators
tukey:Junk scott$ hexdump test.txt
0000000 41 42 43 61 62 63 43
0000007

Wont get very far with just inputting and writing bytes....
However, wont be able to use just a single class for
I/O.
Java divides I/O classes based on functionality.
Some classes can read bytes from files, like FileInputStream
and FileOutputStream. Other classes cant directly access files, but can organize bytes into data types such as
DataOutputStream and DataInputStream. As a programmer it is your responsibility to combine the two.

import java.io.*;
public class Junk {
private static int i;
public static void main(String[] args) {

try {
File file = new File("spoonful.mp3");
FileInputStream fis = new FileInputStream(file);
DataInputStream dis = new DataInputStream(fis);
int i = dis.readInt();
System.out.println(Integer.toBinaryString(i));
9

System.out.println(i);
dis.close();
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
1001001010001000011001100000010
1229206274

Similiar commands to read primitive types readLong(),


readShort(), readFloat(), readDouble(), readByte(), readChar(),
etc

You write primitive data with DataOutputStream.


import java.io.*;
public class Junk {
public static void main(String[] args) {
int i;
double[] d = new double[10];
for (i = 0; i < 10; i++) {
d[i] = Math.random();
}
try {
File file = new File("test.txt");
FileOutputStream fos = new FileOutputStream(file);
10

DataOutputStream dos = new DataOutputStream(fos);


for (i = 0; i < d.length; i++)
dos.writeDouble(d[i]);
dos.close();
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException ioe) {
ioe.printStackTrace();
}
}

This will write a binary file consisting of 10 doubles


or a file of size 80 bytes (10 x 8 bytes (= 64 bits)).
There are other commands to writeChar(), writeInt(),
writeShort(), etc.
$ hexdump test.txt
0000000 3f e4 ce e7
0000010 3f cb 4a 45
0000020 3f e8 8a 01
0000030 3f ea d1 c1
0000040 3f d2 e8 b8
0000050

b1
cf
51
cd
23

80
8a
37
66
35

cc
5c
fa
6c
d6

da
30
c2
8c
6c

3f
3f
3f
3f
3f

e3
e6
eb
ea
e1

6e
c3
a4
a5
04

17
f9
91
50
4a

0e
a8
22
00
f8

97
58
0b
23
08

e9
89
13
dd
19

4f
f3
93
b6
8d

Want to see the values of the binary value, read the


data back in, using, of course, DataInputStream.
import java.io.*;
public class Junk {
public static void main(String[] args) {
int i;
try {
File file = new File("test.txt");
FileInputStream fis = new FileInputStream(file);
DataInputStream dis = new DataInputStream(fis);
//how many doubles are there
int numDoubles = (int) file.length() / 8;

for (i = 0; i < numDoubles; i++)


System.out.println(dis.readDouble());
dis.close();
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException ioe) {
ioe.printStackTrace();
}
}
}

0.6502569643838811
0.6071887288119714
0.21320412286579105
0.7114227569584827
0.7668463312437199
0.8638387360484664
0.8381051074715216
0.8326797487961162
0.2954540580607048

11

Text Files
FileOutputStream and DataOutputStream create binary files.
Binary files are files that are encoded in binary format,
a sequence of binary digits
Text file are easier for humans to read and edit.
To write out ASCII, use PrintWriter which only has two
(overloaded) methods print and println.
PrintWriter wraps File and FileWriter.
Use FileWriter as it operates on character data, rather
than bytes as FileOutputStream does.
12

import java.io.*;
public class Junk {
public static void main(String[] args) {
int i;
try {
File file = new File("test.txt");
FileWriter fos = new FileWriter(file);
PrintWriter pw = new PrintWriter(fos);
pw.println("Four score and seven years ago");
for (i = 0; i < 10; i++)
pw.print(i + " ");
pw.println();
13

pw.println("to be or not to be...");


pw.close();
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException ioe) {
ioe.printStackTrace();
}
}
}

$ less test.txt
Four score and seven years ago
0 1 2 3 4 5 6 7 8 9
to be or not to be...

14

To read text files use FileReader and BufferedReader.


Use FileReader rather than FileInputReader as FileReader
operates on characters rather than bytes.
Since you are reading in strings need to convert data
to appropriate type.
Say you are parsing this data (USD/EUR currency data)
DATE;TIME;VOLUME;OPEN;CLOSE;MIN;MAX
09/16/2011;11:03:01;577;.7238;.7242;.7237;.7244
09/16/2011;11:04:01;738;.7242;.7242;.7241;.7245
09/16/2011;11:05:01;570;.7242;.724;.724;.7245
09/16/2011;11:06:01;500;.7241;.724;.7239;.7243
09/16/2011;11:07:01;602;.724;.7241;.7238;.7244
09/16/2011;11:08:01;496;.7242;.7242;.724;.7244
09/16/2011;11:09:01;588;.7242;.7241;.724;.7245
15

Conversions can be string to type conversions or could


use Scanner class if data allows
import java.io.*;
import java.util.*;
public class ParseData {
public static void main(String[] args) {
File file = new File("data.csv");
FileReader fr = null;
try {
fr = new FileReader(file);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
16

e.printStackTrace();
}
BufferedReader bufRead = new BufferedReader(fr);
String str = null;
ArrayList<Integer> volume = new ArrayList<Integer>(1000);
ArrayList<Float> open = new ArrayList<Float>(1000);
ArrayList<Float> close = new ArrayList<Float>(1000);
ArrayList<Float> min = new ArrayList<Float>(1000);
ArrayList<Float> max = new ArrayList<Float>(1000);
try {
bufRead.readLine(); //skip over first line

while(true) {
if(!bufRead.ready())
break;
str = bufRead.readLine();
//create vectors for open close min max
String data[] = str.split(";");
volume.add(Integer.parseInt(data[2]));
open.add(Float.parseFloat(data[3]));
close.add(Float.parseFloat(data[4]));
min.add(Float.parseFloat(data[5]));
max.add(Float.parseFloat(data[6]));

}
} catch (IOException e) {

// TODO Auto-generated catch block


e.printStackTrace();
}
try {
bufRead.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//average ohlc
ArrayList<Float> avg = new ArrayList<Float>(1000);
for(int i = 0; i < open.size(); i++) {
avg.add((open.get(i) + close.get(i) +
min.get(i) + max.get(i))/4);

System.out.println("avg = " + avg.get(i));


}
}
}
avg
avg
avg
avg
avg
avg
avg
avg
....

=
=
=
=
=
=
=
=

0.724025
0.72425
0.724175
0.72407496
0.724075
0.7242
0.72419995
0.724125

Buffered Input
BufferedReader, BufferedWriter, BufferedInputStream, and
BufferedOutputStream classes add a data buffer to the
stream. Buffers increase the efficiency of reads and
writes. Reading or writing to a hard disk is an expensive operation.
Without buffering, calls to read() or readLine() cause
bytes to be read from a file, converted into characters,
and then returned, each and every time the call is made.
This can be very inefficient. Calls that use a buffer
will read more data than they need into a buffer, and
read() or readline() will read from the buffer not the
underlying file. The buffer handles the refilling of data
as necessary.
17

Buffer work similarly for writes. For output, the file is


not actually written to until the buffer is full or a call
to flush() is made.
You can use the default buffer size, which is determined by the JVM, or you can set how many bytes
you want the buffer to be using the constructor public
BufferedInputStream(InputStream in, int size). The size
variable determines the length of the array used as a
buffer.
BufferedInputStream also lets you mark a location in the
stream, which you can then reset the stream to that position, allowing you to reread the data. To mark a location in the file you use public void mark(int readlimit).

The readlimit variable determines how many bytes you


can read before the mark is forgotten. To return to the
mark, you use the method public void reset(), which
returns you to the mark.
Example of the use of mark/reset would be if you are
parsing data from a file and encounter an error. You
can figure out where the error occurred in the stream as
well as try another approach to dealing with the error.

Writing Objects
One way to store objects is to write them to a file.
To dump objects to a file use ObjectOutputStream
To read objects from a file use ObjectInputStream
To perform Object I/O you need to implement the
Serializable interface, an interface which declares no
methods. So no need to do anything for your class
except say YourClass implements Serializable.
Object serialization converts an object to a stream of
bytes so the state of an object can be saved in a file
and later retrieved.
18

import java.io.*;
public class PointTest
{

public static void main(String[] args) throws IOException, Cla


File file = new File("points.dat");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
19

ObjectOutputStream outObj = new ObjectOutputStream(fos);


Point point;
for (int i = 0; i < 10; i++) {
point = new Point(Math.random()*10, Math.random()*10);
outObj.writeObject(point);
}
outObj.close();
//lets read the point data back in
FileInputStream fis = new FileInputStream(file);
ObjectInputStream inObj = new ObjectInputStream(fis);

for(int i = 0; i<10; i++) {


point = (Point) inObj.readObject();
point.printCoordinates();
}
inObj.close();
}
}

(3.4804772126529935, 9.022468366930763)
(1.115180624647354, 6.661355545294354)
(8.31500257018606, 4.511527449160546)
(7.535963930182138, 3.433276558445134)
(0.8471097037889497, 0.6407571229156239)
(7.98692779668948, 5.196972324932716)
(9.859089403088605, 6.481831922153297)
(9.949606432837793, 5.4348905050162655)
(9.954775484312973, 4.718560591371585)
(3.1619529963846125, 2.374910118125977)

20

Random Access Files


You dont want to start at the beginning of a file and
sequentially move through it.
Use the class RandomAccessFile.
RandomAccessFile implements DataInputStream and DataOutputStream
so can read and write primitive data types.
Since RandomAccessFile accesses files randomly it doesnt
inherit InputStream or OutputStream.
RandomAccessFile files can created from a String pathname of a File object.
21

In addition to the filename, you need to set the fiels


access mode. This is string, and possible modes are
r for opening the file for reading, rw open the file
for both reading and writing, and rwd, which open
the file for reading and writing and require that every
update to the files content be written synchronously
to the underlying storage device.
try {
RandomAccessFile raf = new RandomAccessFile("file", "rw");
}
catch (IOEception e) {
...
}

Opening a file in r mode, requires that the file exisit.


Otherwise a IOException is thrown.
Opening a file in rw mode will create the file if it
doesnt exist.
Once open, then you use the methods of DataInputStream
and DataOutputStream to read and write primitive types.
For example, readInt(), writeInt(), etc.
To get random access use the method seek(long pos).
The long value is the offset from the beginning of the
file.
To know where you currently are in the file, use getFilePointer()
which returns the offset in bytes from the beginning of
the file.

To navigate to the end of the file use length() to get


the size of the file and seek(file.length()) to move to
the end of the file. You can write and seek beyond the
end of the file, but cannot read beyond it.

File Management
The class File gives you access to the file system.
public File(String pathname) creates a new File instance,
converting the string into an abstract pathname.
An abstract pathname has two components:
1) An optional system-dependent prefix string, such as
a disk-drive specifier, / for the UNIX root directory,
or drive letter : \\ for a Microsoft Windows machine
and
22

2) A sequence of zero or more string names.


So to write file names in java, use File.separator, which
is a string representing the sytems seperator, either /
for Unix or \\ for Windows.
String str = File.separator + "Users" + File.separator + "cse";
System.out.println(str);
/Users/cse

Another constructor for File will let you create a File instance from an abstract pathname and a child abstract
pathname. File(String parent, String child)

String dir = File.separator + "Users" +


File.separator + "cse" + File.separator;
System.out.println(dir);
String fname = "Hello.java";
File file = new File(dir, fname);
File objects can be either files or directories. You use
isDirectory() or isFile() methods to tell them apart.
To make a directory you can do something like this
File tmpDir = new File(File.separator + "tmp"
+ File.separator + "junk");
tmpDir.mkDir();

The method list() will return a list (a string array) of


files and subdirectories in a directory. The list includes
hidden files as well.
String dir = File.separator + "Users"
+ File.separator + "cse" + File.separator;
File file = new File(dir);
String [] list = fi.list();
for (int i = 0; i < list.length; i++) {
System.out.println(list[i]);
}
.emacsd
.vimrc

demo
doc
java
junk
junk.c
size.c
...
There are other methods such as listFiles() which just
lists the files in a directory.