The examples we've seen so far have all read or written file content using streams. Streams provide sequential access to data and are particularly useful for network applications, which are often stream-oriented. Files stored on modern hard disks (as opposed to streaming tape drives) need not be accessed sequentially, and Java provides random access to files with the RandomAccessFile class. Example 3-8 demonstrates the use of random-access files by defining a list of strings stored in a file, along with an index to the position of each string. Note the use of writeInt( ), writeLong( ), and writeUTF( ) to write integers, longs, and strings, and the use of readInt( ), readLong( ), and readUTF( ) to read the corresponding values back. These methods are defined by the DataOutput and DataInput interfaces, which are also implemented by the DataOutputStream and DataInputStream classes. Note also the use of the seek( ) method to set the file position of a RandomAccessFile and the getFilePosition( ) method for querying the current position. Finally, don't forget that, like streams, random-access files must be closed when they are no longer needed. Example 3-8. WordList.javapackage je3.io; import java.io.*; /** * This class represents a list of strings saved persistently to a file, * along with an index that allows random access to any string in the list. * The static method writeWords( ) creates such an indexed list in a file. * The class demostrates the use of java.io.RandomAccessFile */ public class WordList { // This is a simple test method public static void main(String args[ ]) throws IOException { // Write command-line arguments to a WordList file named "words.data" writeWords("words.data", args); // Now create a WordList based on that file WordList list = new WordList("words.data"); // And iterate through the elements of the list backward // This would be very inefficient with sequential-access streams for(int i = list.size( )-1; i >= 0; i--) System.out.println(list.get(i)); // Tell the list we're done with it. list.close( ); } // This static method creates a WordList file public static void writeWords(String filename, String[ ] words) throws IOException { // Open the file for read/write access ("rw"). We only need to write, // but have to request read access as well RandomAccessFile f = new RandomAccessFile(filename, "rw"); // This array will hold the positions of each word in the file long wordPositions[ ] = new long[words.length]; // Reserve space at the start of the file for the wordPositions array // and the length of that array. 4 bytes for length plus 8 bytes for // each long value in the array. f.seek(4L + (8 * words.length)); // Now, loop through the words and write them out to the file, // recording the start position of each word. Note that the // text is written in the UTF-8 encoding, which uses 1, 2, or 3 bytes // per character, so we can't assume that the string length equals // the string size on the disk. Also note that the writeUTF( ) method // records the length of the string so it can be read by readUTF( ). for(int i = 0; i < words.length; i++) { wordPositions[i] = f.getFilePointer( ); // record file position f.writeUTF(words[i]); // write word } // Now go back to the beginning of the file and write the positions f.seek(0L); // Start at beginning f.writeInt(wordPositions.length); // Write array length for(int i = 0; i < wordPositions.length; i++) // Loop through array f.writeLong(wordPositions[i]); // Write array element f.close( ); // Close the file when done. } // These are the instance fields of the WordList class RandomAccessFile f; // the file to read words from long[ ] positions; // the index that gives the position of each word // Create a WordList object based on the named file public WordList(String filename) throws IOException { // Open the random access file for read-only access f = new RandomAccessFile(filename, "r"); // Now read the array of file positions from it int numwords = f.readInt( ); // Read array length positions = new long[numwords]; // Allocate array for(int i = 0; i < numwords; i++) // Read array contents positions[i] = f.readLong( ); } // Call this method when the WordList is no longer needed. public void close( ) throws IOException { if (f != null) f.close( ); // close file f = null; // remember that it is closed positions = null; } // Return the number of words in the WordList public int size( ) { // Make sure we haven't closed the file already if (f == null) throw new IllegalStateException("already closed"); return positions.length; } // Return the string at the specified position in the WordList // Throws IllegalStateException if already closed, and throws // ArrayIndexOutOfBounds if i is negative or >= size( ) public String get(int i) throws IOException { // Make sure close( ) hasn't already been called. if (f == null) throw new IllegalStateException("already closed"); f.seek(positions[i]); // Move to the word position in the file. return f.readUTF( ); // Read and return the string at that position. } } |