Table of Contents
Here’s the class hierarchy to support random access files:
(Package java.io)
Object
RandomAccessFile (implements DataInput, DataOutput)
DataInput
ObjectInput
DataOutput
ObjectOutput
Class RandomAccessFile
Instances of class RandomAccessFile support both reading and writing to a random access file. A random access file behaves like a large array of bytes stored in the file system. There is a kind of cursor, or index into the implied array, called the file pointer; input operations read bytes starting at the file pointer and advance the file pointer past the bytes read. If the random access file is created in read/write mode, then output operations are also available; output operations write bytes starting at the file pointer and advance the file pointer past the bytes written. Output operations that write past the current end of the implied array cause the array to be extended. The file pointer can be read by the getFilePointer method and set by the seek method.
It is generally true of all the reading routines in this class that if end-of-file is reached before the desired number of bytes has been read, an EOFException is thrown. If any byte cannot be read for any reason other than end-of-file, an IOException other than EOFException is thrown. In particular, an IOException may be thrown if the stream has been closed.
RandomAccessFile has two constructors:
| Constructor | Description |
|---|---|
public RandomAccessFile(File file, String mode) throws IOException | Creates a random access file stream to read from, and optionally to write to, the file specified by the File argument. |
public RandomAccessFile(String name, String mode) throws FileNotFoundException | Creates a random access file stream to read from, and optionally to write to, a file with the specified name. |
It implements the DataInput and DataOutput interfaces, and so it supports all the methods required by them.
In addition, it implements the following methods:
| Method | Description |
|---|---|
public void close() throws IOException | Closes this random access file stream and releases any system resources associated with the stream. A closed random access file cannot perform input or output operations and cannot be reopened. |
public final FileDescriptor getFD() throws IOException | Returns the opaque file descriptor object associated with this stream. |
public long getFilePointer() throws IOException | Returns the current offset in this file. |
public long length() throws IOException | Returns the length of this file. |
public void seek(long pos) throws IOException | Sets the file-pointer offset, measured from the beginning of this file, at which the next read or write occurs. The offset may be set beyond the end of the file. Setting the offset beyond the end of the file does not change the file length. The file length will change only by writing after the offset has been set beyond the end of the file. |
public void setLength(long newLength) throws IOException | Sets the length of this file.If the present length of the file as returned by the length method is greater than the newLength argument then the file will be truncated. In this case, if the file offset as returned by the getFilePointer method is greater then newLength then after this method returns the offset will be equal to newLength.If the present length of the file as returned by the length method is smaller than the newLength argument then the file will be extended. In this case, the contents of the extended portion of the file are not defined. |
Example
Here’s an example of using the RandomAccessFile class.
Employee
First, let’s create a class that represents an employee, whose data we’ll be saving in a random access file:
package randomAccess;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
/**
* Class to represent an employee.
*
* @author Bryan J. Higgs, 31 January, 2001
*/
public class Employee
{
public static final int RECORD_SIZE = 4 // for the employeeNo
+ 80 // for the name (40 characters)
+ 8 // for the salary
+ 4; // for the managedBy
public static final int NO_MANAGER = -1; // Means no managedBy
/**
* Constructor (used for constructing an object and
* populating it using the read method),
*/
public Employee()
{
}
/**
* Constructor.
*/
public Employee(int employeeID, String name, double salary, int managedBy)
{
m_employeeID = employeeID;
m_name = name;
m_salary = salary;
m_managedBy = managedBy;
}
/**
* Returns the employee's ID.
*/
public int getID()
{
return m_employeeID;
}
/**
* Returns the employee's name.
*/
public String getName()
{
return m_name;
}
/**
* Returns the employee's salary.
*/
public double getSalary()
{
return m_salary;
}
/**
* Returns the employee's manager's ID.
*/
public int getManagedBy()
{
return m_managedBy;
}
/**
* Prints out the employee's information.
*/
public void print()
{
PrintStream out = System.out;
out.println("Employee ID: " + m_employeeID);
out.println(" Name: " + m_name);
out.println(" Salary: $" + m_salary);
out.println(" Managed by: " + m_managedBy);
}
/**
* Writes employee data to the specified file.
*/
public void write(RandomAccessFile file)
throws IOException
{
file.writeInt(m_employeeID);
writeName(file);
file.writeDouble(m_salary);
file.writeInt(m_managedBy);
}
/**
* Reads employee data from the specified file.
*/
public void read(RandomAccessFile file)
throws IOException
{
m_employeeID = file.readInt();
m_name = readName(file);
m_salary = file.readDouble();
m_managedBy = file.readInt();
}
//// Private methods ////
/**
* Writes the employee's name to the specified file.
* The name is truncated or padded to MAX_NAME_SIZE characters.
*/
private void writeName(RandomAccessFile file)
throws IOException
{
StringBuffer buf = new StringBuffer(m_name);
buf.setLength(MAX_NAME_SIZE); // Truncate or pad, as necessary
file.writeChars(buf.toString());
}
/**
* Reads the employee's name from the specified file.
* The name is extracted, any padding removed, and then trimmed down.
*/
private String readName(RandomAccessFile file)
throws IOException
{
StringBuffer buf = new StringBuffer();
for (int i = 0; i < MAX_NAME_SIZE; i++)
{
buf.append( file.readChar() );
}
String name = buf.toString().replace('\0', ' ').trim();
return name;
}
////// Private data ///////
private static final int MAX_NAME_SIZE = 40; // Characters
private int m_employeeID = -1; // Employee id
private String m_name = ""; // Employee name
private double m_salary = 0.0; // Employee salary
private int m_managedBy = -1; // Manager's employee id (-1 if no manager)
}
EmployeeDataLoader
And here’s a class that loads employee data into a random access file:
package randomAccess;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* Class to load a set of employee data to a file.
*
* @author Bryan J. Higgs, 31 January, 2001.
*/
public class EmployeeDataLoader
{
/**
* Main entry point.
*
* @param args command line arguments.
* args[0] is the name of the file to load.
*/
public static void main(String[] args)
{
if (args.length < 1)
{
System.err.println(
"Usage: java randomAccess.EmployeeDataLoader <file-to-load>");
return;
}
// Extract the filename
String fileName = args[0];
// Construct all employees (empID, empName, empSalary, managedBy)
Employee bigBoss = new Employee(1, "Richie Rich", 2000000.0, -1);
Employee cfo = new Employee(2, "Guy Noire", 200000.0, 1);
Employee hrBoss = new Employee(3, "Prescott Hireling", 100000.0, 1);
Employee sectry = new Employee(4, "Dottie Magnusson", 40000.0, 3);
Employee opsBoss = new Employee(5, "Frank Getitdone", 120000.0, 1);
Employee minion = new Employee(6, "Joe Duzit", 30000.0, 5);
RandomAccessFile file = null;
try
{
// Create random access file object for writing.
file = new RandomAccessFile(fileName, "rw");
// Write all employees to the file
// NOTE: The employee ID is the index of the employee data,
// so the employees are written in employee ID order.
bigBoss.write(file);
cfo.write(file);
hrBoss.write(file);
sectry.write(file);
opsBoss.write(file);
minion.write(file);
}
catch (IOException ex)
{
ex.printStackTrace();
}
finally
{
// Close the file, under all circumstances.
if (file != null)
{
try
{
file.close();
}
catch (IOException ex)
{
// Ignore
}
}
}
}
}
EmployeeDataReader
Now, here’s a class that reads the data back in from the file, reading employees sequentially:
package randomAccess;
import java.io.EOFException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* Class to read a set of employee data from a file.
*
* @author Bryan J. Higgs, 31 January, 2001.
*/
public class EmployeeDataReader
{
/**
* Main entry point.
*
* @param args command line arguments.
* args[0] is the name of the file to read.
*/
public static void main(String[] args)
{
if (args.length < 1)
{
System.err.println(
"Usage: java randomAccess.EmployeeDataReader <file-to-read>");
return;
}
// Extract the filename
String fileName = args[0];
RandomAccessFile file = null;
try
{
// Create random access file object for reading
file = new RandomAccessFile(fileName, "r");
// Read the employees, in sequential order.
while (true)
{
Employee emp = new Employee();
emp.read(file);
emp.print();
}
}
catch (EOFException ex)
{
// Do nothing; there are no more records to read
}
catch (IOException ex)
{
ex.printStackTrace();
}
finally
{
// Close file, under all circumstances.
if (file != null)
{
try
{
file.close();
}
catch (IOException ex)
{
// Ignore
}
}
}
}
}
which produces the following output:
Employee ID: 1
Name: Richie Rich
Salary: $2000000.0
Managed by: -1
Employee ID: 2
Name: Guy Noire
Salary: $200000.0
Managed by: 1
Employee ID: 3
Name: Prescott Hireling
Salary: $100000.0
Managed by: 1
Employee ID: 4
Name: Dottie Magnusson
Salary: $40000.0
Managed by: 3
Employee ID: 5
Name: Frank Getitdone
Salary: $120000.0
Managed by: 1
Employee ID: 6
Name: Joe Duzit
Salary: $30000.0
Managed by: 5
EmployeeTree
Finally, here’s how we can do random access to the file.
We now read the file using random access, to build a simple ‘org chart’ of the employees:
package randomAccess;
import java.io.EOFException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* Class to print out a management hierarchy, given an employee ID.
*
* @author Bryan J. Higgs, 31 January, 2001.
*/
public class EmployeeTree
{
/**
* Main entry point.
*
* @param args command line arguments.
* args[0] is the name of the file to read.
* args[1] is the employee ID.
*/
public static void main(String[] args)
{
if (args.length < 2)
{
System.err.println(
"Usage: java randomAccess.EmployeeTree <file-to-read> <empID>");
return;
}
// Extract the filename
String fileName = args[0];
// Extract the employee ID
int empID = Integer.parseInt(args[1]);
RandomAccessFile file = null;
try
{
// Create random access file object for reading
file = new RandomAccessFile(fileName, "r");
// Print the management hierarchy
printTree(file, empID);
// end it with a fresh newline
System.out.println();
}
catch (IOException ex)
{
ex.printStackTrace();
}
finally
{
if (file != null)
{
try
{
file.close();
}
catch (IOException ex)
{
// Ignore
}
}
}
}
/**
* Prints out a management hierarchy, given an employee ID
*/
private static int printTree(RandomAccessFile file, int empID)
throws IOException
{
// Calculate position of employee record in file.
long position = (empID-1) * Employee.RECORD_SIZE;
// Seek to it.
file.seek(position);
// Read employee data
Employee emp = new Employee();
emp.read(file);
// Perform a sanity check
if (emp.getID() != empID)
{
throw new IllegalStateException(
"Employee IDs don't match:" + empID + ", " + emp.getID());
}
int level = 0; // Used for indentation
if (emp.getManagedBy() != Employee.NO_MANAGER)
{
// If employee has a manager, recurse
level = printTree(file, emp.getManagedBy());
// Indent to level
for (int i = 0; i < level; i++)
System.out.print(' ');
System.out.print("manages ");
}
// Print out employee name
System.out.println(emp.getName());
return ++level;
}
}
which produces the following output:
Richie Rich
manages Frank Getitdone
manages Joe Duzit
