134 lines
6.8 KiB
Plaintext
134 lines
6.8 KiB
Plaintext
This is a walkthrough of how to use File I/O. It will give a walkthrough of
|
|
how to read from standard in, and how to read from and write to files.
|
|
|
|
|
|
WRITING AND READING TO FILES
|
|
============================================================================
|
|
|
|
/O stands for "input and output." File I/O is reading from or writing to
|
|
files on the disk. It should be noted that java treats network
|
|
sockets like files as far as IO goes, like UNIX does. Although networking
|
|
is not covered in this class, it is quite similar to accessing files on
|
|
the disk. The standard input, standard error, and standard output are
|
|
also very similar to files, and in fact the types for these three streams
|
|
are found in java.io, along with the classes to perform other file operations.
|
|
|
|
There are many slight variations in what needs to be done to perform
|
|
File IO depending on what type of file is being dealt with and what needs
|
|
to be done with it. We will first focus our attention on reading lines from
|
|
a text file. The first thing that we should do is open the file in a
|
|
FileReader (Note that we are creating a FileReader object):
|
|
|
|
FileReader fr= new FileReader("myFile.txt");
|
|
|
|
The next thing that we should do is to wrap the FileReader in a
|
|
BufferedReader:
|
|
|
|
BufferedReader br= new BufferedReader(fr);
|
|
|
|
This code creates a BufferedReader which will ask fr what is in the file
|
|
and return it to the program line by line.
|
|
|
|
The designers of Java decided to use this wrapper class concept to allow a
|
|
lot of flexibility without having to create hundreds of different classes.
|
|
Sometimes people will accomplish the above in one statement where perhaps
|
|
the "wrapper" idea will be more obvious:
|
|
|
|
BufferedReader has, among other methods, the method
|
|
|
|
String readLine()
|
|
|
|
which returns the next line of text from the file without the '\n' on the
|
|
end- this method also automatically goes to the next line, so the next
|
|
call to readLine() will give the next line, not the same one. When
|
|
the program is done reading from the file, it should call the close method
|
|
on the BufferedReader (which will close the underlying FileReader) so
|
|
that the associated file resources may be freed (note that while
|
|
memory is garbage collected, other resources, such as file descriptors
|
|
are not). The above code, must of course have the exceptions dealt with
|
|
properly, but that will be discussed in a minute.
|
|
|
|
The other major operation that we will concern ourselves with is writing
|
|
lines of text to a file. This can be accomplished similarly via the
|
|
use of the FileWriter and PrintWriter classes:
|
|
|
|
FileWriter fr=new FileWriter("output.txt");
|
|
PrintWriter pr= new PrintWriter(fr);
|
|
|
|
The PrintWriter class has print() and println() methods which are overloaded
|
|
to accept just about any paramter type and write it to the underlying
|
|
FileWriter. It should be noted that print and println do NOT throw
|
|
exceptions if a problem occurs while writing to the underlying file-
|
|
instead, they just set an error variable in the PrintWriter instance,
|
|
which may be checked with the checkError() method. The FileWriter class,
|
|
may throw an exception when created, as described later. The PrintWriter
|
|
must also be closed when the program is finished with it. If an output
|
|
stream in not closed, then not only will the resources not be freed,
|
|
but also, the data will never actually be written out to the disk,
|
|
as it is buffered in memory, and flushed only when a certain amount
|
|
is present, or the close() or flush() method gets called.
|
|
|
|
So why do we need to deal with these extra classes? Why can't we
|
|
just have one class to do it all? And how do these classes interact?
|
|
|
|
Each class does a specific job and provides a certain ammount of modularity
|
|
and abstraction. Lets look at the FileReader and BufferedReader:
|
|
|
|
FileReader- has the job of reading characters from a file and
|
|
assumes that the characters use default encoding.
|
|
|
|
BufferedReader- has the job of reading text from a character-input stream,
|
|
buffering characters so as to provide for the efficient
|
|
reading of characters, arrays, and lines.
|
|
|
|
there are other classes that could be put into a BufferedReader, for example,
|
|
an InputStreamReader- which basically reads from a byte stream and converts
|
|
to a character stream, using some encoding scheme, which may be specified.
|
|
So for example, if the file that you wanted to read used a different Unicode
|
|
encoding scheme than the default for your system (maybe you are writing
|
|
some international buisness software), then you would instead need to
|
|
make a FileInputStream, wrap that in an InputStreamReader, then wrap that
|
|
in a BufferedReader. In general, "Stream" deals with bytes, whereas
|
|
"Reader" deals with characters.
|
|
|
|
This abstraction gives the input classes more flexibility, in that
|
|
they can also be used to allow for reading from other input streams
|
|
than those avaiable from files on the disk- a network connection
|
|
provides an instance of InputStream to read incoming data from
|
|
the connection- InputStreams (as just mentioned) deal with bytes-
|
|
if the program wants to read lines of text, for example, the same
|
|
wrapping can be performed with the same classes to get a BufferedReader
|
|
to give lines of text at a time from the network
|
|
|
|
|
|
|
|
WRITING AND READING OBJECTS TO FILES
|
|
============================================================================
|
|
Writing and reading java objects to a file are slightly different than writing
|
|
and reading Strings to file. First off, in order for a java Object to be able
|
|
to be written to a file, it must implement Serializable. Only classes which
|
|
implement the Serializable interface can be written to a file as an Object.
|
|
|
|
To write an object to a file, you first need to create the Streams.
|
|
The reason that you cannot use PrintWriter as you used before is because
|
|
PrintWriter only can write Strings to a file.
|
|
The two streams that need to be created are
|
|
FileOutputStream and ObjectOutputStream.
|
|
|
|
|
|
The constructor to FileOutputStream takes in the name of the file to write to as
|
|
a String. If the file does not exist, it creates one.
|
|
The constructor to ObjectOutputStream takes in the newly created
|
|
FileOutputStream.
|
|
After creating them, you can write the object to the ObjectOutputStream.
|
|
If the ObjectOutputStream was called oos, then oos.writeObject(theObject);
|
|
would write the object to file.
|
|
After writing all object, make sure to flush the ObjectOutputStream and close
|
|
the FileOutputStream.
|
|
|
|
To read objects from a file, you need to create an ObjectInputStream and a
|
|
FileInputStream. The constructors for these work similar to the writer.
|
|
To actually read an object, do ois.readObject(). This will need to be casted
|
|
to the actuall object that it is. If the file does not exist, it will throw
|
|
a FileNotFoundException. When the end of the file is reached, it throws
|
|
an EndOfFileException. |