Survey
* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project
Book 6 - File Manipulation
By itself, a file consists of nothing more than a series of
related bytes or data located on a persistent (lasting) medium
such as a hard disk.
Files are used so that information accessed and generated by
an application is not lost once the application finishes
executing or once a computer is turned off.
A file is separate from the application accessing it and can be read from and written to
by more than one application. Most applications require access to one or more files.
As usual, there are many choices in Java when needing to set up a file. The
programmer decides based on the situation and ease of use.
Sequential files
Random Access files
Object Serialization files
SQL files (Structured Query Language for databases)
Classes that you might consider include:
File class
RandomAccessFile class
FileReader and FileWriter classes
Book 6 – File Manipulation - 1
The File Class
Purpose - used for creating an object that represents a file or folder.
Library – from the java.io package
Useful methods:
File (String filename)
– constructor that creates a File object that refers to the file filename
– filename is given as a string; if a path is included, the escape sequences (\\)
must be used to separate drive, folder and files
– E.g. File myFile = new File (“supplies”);
File myFile = new File (“c:\\temp\\supplies”);
createNewFile()
– creates a new file using the file name specified in the constructor if the file does
not already exist
– returns true if the file is created, false otherwise
– throws an IOException exception if the file cannot be created
– enclose in a try/catch structure
– E.g. File textFile = new File ("c:\\temp\\supplies”);
try
{
textFile.createNewFile (); //Create the file named supplies
System.out.println ("File created.");
}
catch (IOException e)
{
System.out.println ("File could not be created.");
System.out.println ("Exception: " + e.getMessage ());
}
Book 6 – File Manipulation - 2
mkdir()
– creates a new folder/directory using the file name specified in the constructor if
the folder/directory does not already exist
– returns true if the folder is created, false otherwise
– note that if the system has a security manager, the folder will not be created
– E.g. File myFolder = new File (“Novel Study”);
boolean success = myFolder.mkdir(); //Create folder named Novel Study
if (success == true)
{
System.out.println(“Folder successfully created.”);
}
Additional Methods of Interest
delete() – permanently deletes the file/folder represented by the file objects. Returns
true if file/folder is deleted, false otherwise
exists() – returns true if the file/folder represented by the File object exists, false if
otherwise
getAbsolutePath() – returns the full absolute pathname string of the file/folder
represented by the File object
canRead() – returns true if the application can read the file represented by the File
object; returns false otherwise
canWrite() – returns true if the application can write to the file represented by the File
object; returns false otherwise
renameTo() – renames the file represented by the File object
isDirectory() – returns true if the file denoted by the File object is a directory/folder;
returns false otherwise
list() – returns a String array containing the files and folders in the directory/folder
denoted pathname of the file in the File object
length() – returns a long number which is the length in bytes of the file denoted by the
File object
*** Many more methods can be explored through Java Help. Look up the File class
and see what is available.
Book 6 – File Manipulation - 3
Using Sequential File Access
Sequential access works best when you want to process files consisting only of text,
such as the files created with a typical text editor — that is, files in which data is not
divided into a series of meaningful blocks, such as records.
Sequential access may not be well suited for storing long series of numbers, because
each number is stored as a character string. A four-digit number would require 4 bytes
of storage instead of the 2 bytes it requires to store the same number as an integer.
Reading a Text File using Sequential Access
A data/text file in Java is opened by instantiating a BufferedReader object made
of a FileReader object. Recall that the BufferedReader class is the same class
used for keyboard input. The use of the FileReader constructor links reading to a
file.
Format:
BufferedReader readerName = new BufferedReader(new FileReader (fileName));
Where readerName is the identifier for the BufferedReader object
Where fileName is a string that specifies the complete filename
including its 3-letter extension (usually .txt)
E.g. BufferedReader bankRFile = new BufferedReader(new FileReader ("bankAccts.txt"));
Now each line in the file can be read using the readLine() method, which reads
a single line in a file as a string:
dataVariable = readerName.readLine();
E.g.
double savingsAccount = Double.parseDouble(bankRFile.readLine());
When a file is first open, a file pointer points at the very beginning of the
data.
For each readLine() executed, the pointer advances to the next line of data
in the file.
Book 6 – File Manipulation - 4
Writing to a Text File using Sequential Access
A data/text file in Java is opened by instantiating a PrintWriter object constructed
of a FileWriter object. The FileWriter constructor directly associates the data file
with the PrintWriter object.
Note that the data file will be created in the same folder as the class file unless a
different path is specified.
Format:
PrintWriter writerName = new PrintWriter(new FileWriter (fileName));
Where writerName is the identifier for the PrintWriter object
Where fileName is a string that specifies the complete filename
including its 3-letter extension (usually .txt)
E.g. PrintWriter bankWFile = new PrintWriter (new FileWriter ("bankAccts.txt"));
Now each line of data can be written as a string to the file using the println()
method:
writerName.println(data);
E.g.
bankWFile.println(Double.toString(chequingAccount));
Closing a File
Though files associated with a Java application are automatically closed once the
application finishes execution, it is always a good habit to close a file in code.
This will help to prevent accidental corruption of the data.
Use the close() method to close either the BufferedReader/PrintWriter object.
writer/readerName.close()
E.g.
bankWFile.close();
Checking that a File Still has Data
The ready() method determines if an input buffer is empty or not or if bytes are
available for reading from the underlying byte stream. The method returns true
if there is more to read. A return of false means there is no more data.
E.g.
if (bankRFile.ready () == false)
Book 6 – File Manipulation - 5
System.out.println(“There is no more data.”);
Creating and Using Random Access Files
A random or direct access file is used to keep records of data grouped into fixed lengths. It
allows for easy access by directly going to a particular record without having to start at the
beginning of a file and sequentially go through all the preceding records.
What
-
is a Record?
a grouped set of data arranged in a series of fields
each record is a fixed length
each field is a fixed length
E.g. Below is a pictorial view of a what a student record might look like:
Last Name:
Smith
First Name: Maria
Birthdate:
1993/04/12
Gender:
F
Credits:
16
Record
Fields
Fieldnames
Every data type reserves a specific number of bytes. The
table to the right lists some of the sizes. When setting up
a record, any string fields will have to be limited in size in
order to make sure each record is a fixed size.
File Pointer – or file position pointer that holds the byte
number of the next file position to be used.
Type
Byte
Short
Int
Float
Long
Double
Char
String
Size in Bytes
1
2
4
4
8
8
2
2 per character
Once the size of the record is known, you can set the file pointer to specific locations in the
file. For example, if the record size is 94 bytes (25 chars for book title, 20 characters for
author, an integer for year published):
- Record 1 will start at byte 0
The Kite Runner
Khaled Hosseini 2006
- Record 2 will start at byte 94
Atonement
Ian McEwan
2001
- Record 3 will start at byte 188
The Tipping Point
Malcolm Gladwell 2000
…
- And so on…
Book 6 – File Manipulation - 6
Defining the Record
Think of a record as an object that is instantiated from a class that defines its fields. This
means, you can create a class in java that defines the record.
For example, suppose we need to make a student record modeling the one on the previous
page:
public class StudentRecord
{
//Declare the record’s fields
protected String lastName;
protected String firstName;
protected String birthDate;
protected char gender;
protected int credits;
//20 chars
//10 chars
//10 chars
//1 char
//Declare the record size
protected final int recSize = 86;
}
After the record’s fields are defined, make a constructor to create the record. A constructor
MIGHT look like:
public StudentRecord(String lastName, String firstName, String birthDate, char gender, int credits)
{
this.lastName = lastName;
Too bad this won’t
this.firstName = firstName;
work properly! :-(
this.birthDate = birthDate;
this.gender = gender;
this.credits = credits;
}
What happens if the first name or last name or any of the other String-type parameters are
not exactly the right size? Remember that it is imperative that all records are the
same size. Varied name sizes will cause each record to be a different size. Unfortunately
there is no quick method that sets the size of a String object. One solution is to incorporate a
StringBuffer object.
Characteristics of a StringBuffer Object
Part of the java.lang package is automatically imported into every program
Has a memory buffer capable of holding a string
The length of a string in a StringBuffer can be identified by the length property
The length of a string in a StringBuffer object can be changed using the setLength()
method (changes size of the buffer)
If a string becomes too long, it is truncated (extra characters cut off at the right)
Some
Book 6 – File Manipulation - 7
If a string is shorter than the size of the buffer, extra characters (‘\u0000’) pad the
string so that the buffer is filled
The capacity() method will provide the capacity of a StringBuffer object (capacity is
the length of the string in the StringBuffer plus 16 – extra positions for reasonable
modification without having to allocate any new memory locations)
The program below illustrates these features of the StringBuffer object:
public class StringBufferDemo
{
public static void main (String[] args)
{
int sb1Cap, sb1Len;
StringBuffer sb1;
sb1 = new StringBuffer ("Hello there, friend!");
System.out.println ("The string buffer contains: " + sb1);
sb1Len = sb1.length ();
System.out.println ("The length of the string in the buffer: " + sb1Len);
sb1Cap = sb1.capacity ();
System.out.println ("The string buffer's capacity is: " + sb1Cap);
System.out.println("--------------------------------");
sb1.setLength (7);
System.out.println ("The string buffer contains: " + sb1);
sb1Len = sb1.length ();
System.out.println ("The length of the string in the buffer: " + sb1Len);
sb1Cap = sb1.capacity ();
System.out.println ("The string buffer's capacity is: " + sb1Cap);
System.out.println("--------------------------------");
sb1.setLength (30);
sb1 = new StringBuffer ("Goodbye to you my friend.");
System.out.println ("The string buffer contains: " + sb1);
sb1Len = sb1.length ();
System.out.println ("The length of the string in the buffer: " + sb1Len);
sb1Cap = sb1.capacity ();
System.out.println ("The string buffer's capacity is: " + sb1Cap);
System.out.println("--------------------------------");
}//main method
}// StringBufferDemo class
Book 6 – File Manipulation - 8
The output resulting in running the program is:
The string buffer contains: Hello there, friend!
The length of the string in the buffer: 20
The string buffer's capacity is: 36
-------------------------------The string buffer contains: Hello t
The length of the string in the buffer: 7
The string buffer's capacity is: 36
-------------------------------The string buffer contains: Goodbye to you my friend.
The length of the string in the buffer: 25
The string buffer's capacity is: 41
--------------------------------
So, to ensure that the strings in a record are set to a specific size, do the following:
StringBuffer tempString = new StringBuffer(stringFieldData);
tempString.setLength(requiredStringFieldLength);
this.stringField = tempString.toString();
Now the constructor looks like:
public StudentRecord(String lastName, String firstName, String birthDate, char gender, int credits)
{
StringBuffer tempString = new StringBuffer(lastName);
tempString.setLength(20);
this.lastName = tempString.toString();
tempString = new StringBuffer(firstName);
tempString.setLength(10);
this.firstName = tempString.toString();
tempString = new StringBuffer(birthDate);
tempString.setLength(10);
this.birthDate = tempString.toString();
this.gender = gender;
this.credits = credits;
}//constructor 1
Book 6 – File Manipulation - 9
Hey! You could
make your own
handy method to
handle the sizing of
the string! ;-)
After the constructor, place the mutator(set) and accessor(get) methods for every field
created for the record. Then, the record definition is done!
public String getLastName()
{
return this.lastName;
}
public void setLastName(String lastName)
{
StringBuffer tempString = new StringBuffer(lastName);
tempString.setLength(20);
this.lastName = tempString.toString();
}
public String getFirstName()
{
return this.firstName;
}
public void setFirstName(String firstName)
{
StringBuffer tempString = new StringBuffer(firstName);
tempString.setLength(10);
this.firstName = tempString.toString();
}
public String getbirthDate()
{
return this.birthDate;
}
public void setBirthDate(String birthDate)
{
StringBuffer tempString = new StringBuffer(birthDate);
tempString.setLength(10);
this.birthDate = tempString.toString();
}
public char getGender()
{
return this.gender;
}
public void setGender(char gender)
{
this.gender = gender;
}
{
return this.credits;
}
public void setCredits(int credits)
{
this.credits = credits;
}
public int getCredits()
Book 6 – File Manipulation - 10
Writing to the Random Access File
Once the record definition is done, record objects can be instantiated in a client program and
written to a random access file. In the client program, records are created via a call to the
constructor.
import java.io.*;
public class Student
{
public static void main (String args[]) throws IOException
{
StudentRecord[ ] studs = new StudentRecord [5];
System.out.println ("Creating Five Friend Records");
studs [0] = new StudentRecord ( "Smith", "Amy","23/02/1991", 'f', 16);
studs [1] = new StudentRecord ( "Jones", "Mary","12/11/1992", 'f', 8);
studs [2] = new StudentRecord ( "Carter", "Carl","15/07/1992", 'm', 16);
studs [3] = new StudentRecord ( "Mayer", "Bob","20/12/1993", 'm', 8);
studs [4] = new StudentRecord ( "Roy", "Tanya","07/08/1991", 'f', 22);
…
Now the records will need to be written directly to the random access file. To open the file,
use the RandomAccessFile constructor from the RandomAccessFile class that exists in Java.
This constructor requires two arguments:
the name of the file under which the data will be stored,
what the file is opened for (“r” – read only, “rw” – reading and writing)
For example:
RandomAccessFile studentFile = new RandomAccessFile ("student_info", "rw");
Normally when a file is first opened, the file pointer positions itself at
the beginning or at byte 0. But, it is a good habit to position the
pointer in code as well. Use the seek() method to do this:
studentFile.seek (0);
Specific byte
location
Book 6 – File Manipulation - 11
Now the file is ready to be written to. To enter each complete record to the file, each
individual field must be written in a specific order. Having an array makes it easier to do the
writing:
//For loop to go through each record in the record array
for (int index = 0 ; index < studs.length ; index++)
{
//Write the student's last name
studentFile.writeChars (studs [index].lastName);
//Write the student's first name
studentFile.writeChars (studs [index].firstName);
//Write the student's birthdate
studentFile.writeChars (studs [index].birthDate);
//Write the student's gender
studentFile.writeChar (studs [index].gender);
//Write the student's earned credits
studentFile.writeInt (studs [index].credits);
System.out.println ("Record " + index + " processed");
System.out.println ("Pointer now at: " + studentFile.getFilePointer ());
}
To write to the file, use the methods:
writeInt(integerValue) –to write an integer value
writeLong(longValue) –to write a long value
writeDouble(doubleValue) –to write a double value
writeChar(byteValue) –to write a single character
writeChars(StringValue) –to write a string
Note that there are more write style methods in the RandomAccessFile class – just look in
Java Help.
The method: getFilePointer() provides the current byte position of the pointer.
Lastly, don’t forget to close the file. Even though the file will automatically close when the
program finishes execution, it is a good habit to write it into the code using the close()
method:
studentFile.close ();
Book 6 – File Manipulation - 12
Reading from the Random Access File
To open the file, use the RandomAccessFile constructor from the RandomAccessFile class that
exists in Java, as we did before.
This constructor requires two arguments:
the name of the file under which the data will be stored,
what the file is opened for (“r” – read only, “rw” – reading and writing)
For example:
RandomAccessFile studentFile = new RandomAccessFile ("student_info", "r");
To read blocks of data from the file, use the methods:
readInt() –reads an integer value – 4 bytes
readLong() –reads a long value – 8 bytes
readDouble() –reads a double value – 8 bytes
readChar() –reads a single character – 2 bytes
Other read methods can be found in the Java Help under the RandomAccessFile Class.
Normally when a file is first opened, the file pointer positions itself at the beginning or at byte
0. But you can also go directly to the beginning of a particular record.
Use the seek() method to do this:
studentFile.seek (bytePosition);
An example:
import java.io.*;
import java.io.RandomAccessFile; //Specific class for random access files
public class RandomFileReadSample
{
public static void main (String args[]) throws IOException
{
long position, recordNum;
BufferedReader kb = new BufferedReader (new InputStreamReader (System.in));
//Make a temporary record
StudentRecord student = new StudentRecord ("", "", "", 'x', 0);
//Open the file for reading
RandomAccessFile studentFile = new RandomAccessFile ("student_info", "r");
//Neat calculation to determine the number of records
Book 6 – File Manipulation - 13
recordSize * (whichRecord - 1);
//length() gives the number of bytes in a file
long numRecords = studentFile.length () / student.recSize;
System.out.println ("There are " + numRecords + " records in the StudentInfo file.");
//Let the user request a specific record
System.out.println ("Which record do you want to see? (1 to " + numRecords + "):");
recordNum = Long.parseLong (kb.readLine ());
//Calculate the byte position of the record
position = student.recSize * (recordNum - 1);
//Position the pointer to the beginning of the record
studentFile.seek (position);
//Read in the first set of characters for the last name
String temp = "";
for (int x = 1 ; x <= student.getLastName ().length () ; x++)
temp = temp + Character.toString (studentFile.readChar ());
student.setLastName (temp);
//Read in the second set of characters for the first name
temp = "";
for (int x = 1 ; x <= student.getFirstName ().length () ; x++)
temp = temp + Character.toString (studentFile.readChar ());
student.setFirstName (temp);
//Read in the set of characters for the birth date
temp = "";
for (int x = 1 ; x <= student.getBirthDate ().length () ; x++)
temp = temp + Character.toString (studentFile.readChar ());
student.setBirthDate (temp);
//Read in the single character for the gender
student.setGender (studentFile.readChar ());
//Read in the integer for the credits
student.setCredits (studentFile.readInt ());
//Output the info
System.out.println ("Information on Record: " + recordNum);
System.out.println(student);
}//main method
}//RandomFileReadSample Class
Book 6 – File Manipulation - 14
Using Object Serialization with Files
It is possible to store objects directly in a data file. This process is generally known as object
serialization:
Definition of Object Serialization
used to persist Java objects to a file or database (persist – refers to long-term storage
of information after a program exits)
“flattens” objects into an ordered, or serialized stream of bytes that can be read at a
later time to recreate the original objects
stores all the values in an object’s properties or variables to a file
Disadvantages to Object Serialization
offers no support for queries (looking up specific records based on field values)
does not allow sharing among multiple users
partial reads or updates are not supported
not suitable for large-capacity data storage
The Serializable Interface
Java provides classes to support writing objects to streams (serialization) and restoring
objects from streams (de-serialization). The “java.io.Serializable” interface has no
methods or fields. However, only objects that implement this interface can be serialized or
de-serialized.
Therefore, a class definition will need to include “implements Serializable”:
import java.io.*;
public class Employee implements Serializable
{
//Fields or properties
private String empName;
//Employee name
private double empSalary;
//Employee annual salary
//Constructor
public Employee (String empName, double empSalary)
{
this.empName = empName;
this.empSalary = empSalary;
}
}
This serializable interface is designed to allow the object's properties to store or access data
from a file using an ObjectOutputStream or an ObjectInputStream.
Book 6 – File Manipulation - 15
Writing the Object to a File using Output Object Streams
An ObjectOutputStream can be used to write objects as well as primitive data in a
platform-independent manner to another stream, such as a file stream. If this is the case, it
is chained (or attached) to FileOutputStream which is a low-level stream that handles
bytes of data.
Using the writeObject method saves the state of the class by writing the individual fields to
the ObjectOutputStream.
import java.io.*;
public class PayRoll
{
public static void main (String args[ ]) throws Exception
{
Employee[] empArray = new Employee [5];
//Array of employee objects
empArray [0] = new Employee ("Allen, Sara", 84559.00);
//Create five employee objects
empArray [1] = new Employee ("Bretson, John", 42510.00);
empArray [2] = new Employee ("Clark, Abe", 63192.00);
empArray [3] = new Employee ("Fletcher, Marg", 56740.00);
empArray [4] = new Employee ("Green, Niles", 75600.00);
//Declare the data file that will hold the employee names and salaries
File dataFile = new File ("empPayroll.dat");
//Set up the required streams to write to the file
FileOutputStream fos = new FileOutputStream (dataFile);
ObjectOutputStream saveEmp = new ObjectOutputStream (fos);
//Write the objects to the file
for (int i = 0 ; i < empArray.length ; i++)
saveEmp.writeObject (empArray [i]);
//Use the writeObject method
//close the stream
saveEmp.close ();
}
}
Some Points
Once the ObjectOutputStream is created and attached to the FileOutputStream,
then the writeObject() method can be called to write employee to the file.
Individual properties within the object's variables are not written separately
The object is saved, as a complete entity
Any data or properties that the object contains are bundled into the object and saved
when the object is saved.
Book 6 – File Manipulation - 16
Reading the Object from a File using Input Object Streams
An ObjectInputStream can be used to load Java objects from a stream where the objects
were saved using an ObjectOutputStream. It is chained to FileInputStream which is a lowlevel stream that handles bytes of data.
Using the readObject method deserializes the object from the object input stream.
import java.io.*;
public class ReadPayroll
{
public static void main (String args[]) throws Exception
{
Employee[ ] empArray = new Employee [5];
//Declare the data file that holds the employee names and salaries
File dataFile = new File ("empPayroll.dat");
//Set up the required streams to read from the file
FileInputStream fis = new FileInputStream (dataFile);
ObjectInputStream readEmp = new ObjectInputStream (fis);
//Read the objects from the file
for (int i = 0 ; i < empArray.length ; i++)
{
//Cast the object when it's read
empArray [i] = (Employee) readEmp.readObject ();
}
//Display the objects
for (int i = 0 ; i < empArray.length ; i++)
{
System.out.println ("Employee " + (i + 1) + ": " + empArray [i] + "\n");
}
readEmp.close ();
}
}
Some Points
Since the ObjectInputStream passes an object along, it doesn't really know what the
object is supposed to be.
Using the cast (Employee) properly identifies the object and passes it to the identifier
(empArray [i]).
Recall: Casting is setting a data type by placing the type name in brackets in front of
the data. E.g. num = (int)(12.78/2.4);
Book 6 – File Manipulation - 17