File and Streams

Persistent data is stored in files, which are stored on hard disks, flash drives, etc. This website discusses the following ways to access a file:

A file itself is just a bunch of related records, companies payroll, stocks, etc. Files themselves can grow to a very large size normally determined by the operating system. There are many ways of organizing records in a file, the common type is called sequential file in which records are typically stored in order by the record-key field. In some instanaces records could be stored in a database but even a database stores its data in files.

File and Streams

Java views each file as a sequential stream of bytes, each file ends with either an end-of-file marker or at a specific byte number record. Three streams are automatically created when we begin executing a Java program

The java.io package contains many classes and interfaces that relate to Java I/O a portion of it is below

java.lang.Object       The root of the class hierarchy
  File     An abstract representation of file and directory pathnames
  FileDescriptor     Serves as an opaque handle to the underlying machine-specific structure representing an open file, open socket or another source or sink of bytes.
  InputStream     This abstract class is the superclass of all classes representing an input stream of bytes.
    ByteArrayInputStream   contains an internal buffer that contains bytes that may be read from the stream
    FileInputStream   obtains input bytes from a file in a file system
    FilterInputStream   is the superclass of all filter input streams, uses its source to possibility tranform data or providing additional functionality
      BufferedInputStream has the ability to buffer the input to support the mark and reset methods
      DataInputStream lets an application read primitive data types from an underlying input stream in a machine-independent way.
      PushbackInputStream

adds functionality to another input stream namely the ability to "push back" or "unread" one byte

PushbackInputStreams are used by programs like compilers that parse there inputs.

    ObjectInputStream   deserializes primitive data and objects previously written using an ObjectOutputStream
    PipedInputStream   provides whatever data bytes are written to the piped output stream
    SequenceInputStream   represents the logical concatenation of other input streams, basically reads a file then moves on the next file and so on.
  OutputStream     This abstract class is the superclass of all classes representing an output stream of bytes.
    ByteArrayOutputStream   implements an output stream in which the data is written into a byte array. The buffer will grow automatically as data is written to it.
    FileOutputStream   an output stream for writing data to a file or a filedescriptor.
    FilterOutputStream   is the superclass of all filter output streams, uses its source to possibility tranform data or providing additional functionality
      BufferedOutputStream an application can write bytes to the underlying output stream without causing a call to the underlying system for each byte written
      DataOutputStream lets an application write primitive data types to an output stream in a machine-independent way
      PrintStream

has the ability to print representations of various data values conveniently.

this is used for performing output to the screen (System.out, System.err)

    ObjectOutputSteam   writes primitive data and objects types to an ObjectOutputStream
    PipedOutputStream   can connect to a piped input stream to create communications pipe
  RandomAccessFile    

Instances of this class supports read and writing to a random access file

Used for direct-access applications such as transaction-processing applications (airline-reservations, point-of-sales, etc), direct-access application provide rapid access to specific data items in large files, this means data is supplied quickly so customers do not have to wait long for an answer.

  Reader     abstract class for reading character streams
    BufferedReader   read text from a character input-stream, buffering characters so as to provide for the efficient reading of characters, arrays and lines.
      LineNumberReader A buffered character-input stream that keeps track of line numbers
    CharArrayReader   this class implements a character buffer that can be used as a character-input stream
    FilterReader   abstract class for reading filtered character streams
      PushbackReader character-stream reader that allows characters to be pushed back into the stream
    InputStreamReader   is a bridge from bytes streams to character streams
      FileReader used for reading streams of characters
    PipedReader   piped character-input stream
    StringReader   character stream whose source is a string
  Writer     abstract class for writing character streams
    BufferedWriter   write text to a character output-stream, buffering characters so as to provide for the efficient writing of characters, arrays and lines.
    CharArrayWriter   this class implements a character buffer that can be used as a character-output stream
    FilterWriter   abstract class for writing filtered character streams
    OutputStreamWriter   is a bridge from character streams to byte streams
      FileWriter class for writing character files
    PipedWriter   piped character-output streams
    PrintWriter   print formatted representations of objects to a text-output stream
    StringWriter   a character stream that collects it output in a string buffer which can then be used to construct a string

Sequential File Access

The below example demonstrates how to write and read from a sequential file. Althrough you could make the below example a lot more better i tried to make it as simple as possible thus highlighting the reading and writing to a sequential file.

Data that is formatted and written to a sequential-access file cannot be modified without reading and writing all the data in the file, it is possible but very awkward. As you can see this type of file is not very good at instant access, like airline reservations, point-of-sales, etc.

The below example demonstrates how to access a sequential file, the program code could be improved but i have tried to make it very simple even if it means using duplicate code. Pay attention to the code in bold this the important sequential-access file stuff.

Sequential File Access import java.io.*;

public class phoneBook {
   private ObjectOutputStream output;
   private ObjectInputStream input;

   File fileName = new File("d:\\java\\data\\phone.dat");

   public static void main(String[] args) {
      phoneBook pb = new phoneBook();
      pb.writeFile(); // open, write and close the file
      pb.readFile(); // open, read and close the file
   }

   public void writeFile() {

      // I could have put this into an array which would have told me how many
      // records i have, it could then have be used in the readFile method below
      // but lets keep things very simple
      Record r1 = new Record("Paul Valle", "0207-568-789");
      Record r2 = new Record("Lorraine Valle", "0207-345-356");
      Record r3 = new Record("Dominic Valle", "0207-765-693");
      Record r4 = new Record("Jessica Valle", "0207-789-876");

      try {
         // Open a file handle for writing
         output = new ObjectOutputStream( new FileOutputStream( fileName));

         // Write some data to the file it could throw
         // InvalidClassException or NotSerializableException exceptions
         output.writeObject( r1 );
         output.writeObject( r2 );
         output.writeObject( r3 );
         output.writeObject( r4 );


         // Flush the ObjectOutputStream. This will write any buffered
         // output bytes and flush through to the FileOutputStream
         output.flush();

         // Close the file
         output.close();
      } catch (InvalidClassException icex) {
         System.out.println("Invalid Class");
      } catch (NotSerializableException nsex) {
         System.out.println("Object is not serializable");
      } catch (IOException e) {
         System.out.println("Problems either flushing or closing file");
      }
   }

   public void readFile() {
      Record r; // this object will hold the records when retrieved from the file

      try {
         // Open the file handle for reading
         input = new ObjectInputStream( new FileInputStream(fileName));

         // I know i have 4 records so lets read them, this is where i could have used the array
         // by using the length of the array i would have know how many records i have.
         for (int i = 0; i < 4; i++ ) {
            // Here we implicity cast the retrieved Object
            r = ( Record ) input.readObject();
            System.out.println("Name: " + r.getName() + " Phone: " + r.getPhone() );
         }

         // Close the file
         input.close();

      } catch (EOFException eofex) {
         System.out.println("No more records to read");
      } catch (ClassNotFoundException cnfex) {
         System.out.println("Unable to create object - class not found");
      } catch (IOException e ) {
         System.out.println("Unable to close file");
      }
   }
}

// Serialization involves saving the current state of an object to a stream,
// and restoring an equivalent object from that stream.
class Record implements Serializable {

   private String name;
   private String phone;

   // Constructor
   public Record() { this ("", ""); }

   // Overloaded Constructor
   public Record(String n, String p) {
      name = n;
      phone = p;
   }

   // The get and set methods
   public void setName(String n) { name = n; }

   public void setPhone(String p) { phone = p; }

   public String getName() { return name; }

   public String getPhone() { return phone; }
}

Random File Access

Many airline reservations and point-of-sales systems use random access files, you can acess the files directly and quickly without searching through all the other records first. With random access files you need to create the structure, normally you make sure that all records are the same length but there are other techinques as well. The file structure is like a railroad train which has many carts (all the same size), some empty and some that contain contents. Data can be inserted without destroying other data, also data can be updated and deleted without having to re-create the file.

The below example demonstrates how to access a random file, the program code could be improved but i have tried to make it very simple even if it means using duplicate code. Pay attention to the code in bold this the important random-access file stuff.

Random File access

import java.io.*;

public class phoneBook2 {
   private RandomAccessFile file;

   public static void main(String[] args) {

      phoneBook2 pb = new phoneBook2();
   }

   public phoneBook2() {
      createFileStructure();
      writeRecord();
      readRecord();
   }

   private void createFileStructure() {
      Record r = new Record();

      try {
         // Create the file handle and open the file (read/write mode)
         File fileName = new File("d:\\java\\data\\phoneBook2.dat");
         file = new RandomAccessFile( fileName, "rw" );


         // Create 100 blank records in the file
         for ( int i = 0; i < 100; i++)
         r.write( file );

         System.out.println("Written 100 Blank records\n");

      } catch ( IOException ioex ){
         System.out.println("File does not exists");
      }
   }

   private void writeRecord() {
      int recordNumber = 37; // using a fixed record number
      Record r = new Record();

      try {
         // Create the file handle and open the file (read/write mode)
         File fileName = new File("d:\\java\\data\\phoneBook2.dat");
         file = new RandomAccessFile( fileName, "rw" );


         // Write the record to the file
         if ( recordNumber > 0 && recordNumber < 100 ) {
            r.setName("Paul Valle");
            r.setPhone("0207-876-8765");

            // Go to the record 37 location (bucket)
            file.seek( (recordNumber - 1) * r.size() );

            // Write the record out
            r.write( file );

            System.out.println("Updated record 37\n");
         }
      } catch ( IOException ioex ){
         System.out.println("File does not exists");
      }
   }

   private void readRecord() {
      int recordNumber = 37;
      Record r = new Record();

      try {
         // Create the file handle and open the file (read only mode)
         File fileName = new File("d:\\java\\data\\phoneBook2.dat");
         file = new RandomAccessFile( fileName, "r" );

         // Jump to the file location using seek
         file.seek( (recordNumber - 1) * r.size() );

         // Display the offset
         System.out.println("File offset position: " + file.getFilePointer() );
         System.out.println("File Length: " + file.length() );

         // Read the record
         r.read( file );

         // Display the record
         System.out.println("Record:37 - Name: " + r.getName() + " Phone: " + r.getPhone() );
      } catch ( IOException ioex ){
         System.out.println("File does not exists");
      }
   }
}

class Record {
   private String name;
   private String phone;

   // Constructor
   public Record() { this ("", ""); }

   // Overloaded Constructor
   public Record(String n, String p) {
      name = n;
      phone = p;
   }

   // Public method to read the data from the file
   public void read( RandomAccessFile file ) throws IOException {
      setName( padData( file ) );
      setPhone ( padData( file ) );

   }

   // Public method to write the data to the file
   public void write( RandomAccessFile file ) throws IOException {
      writeData( file, getName() );
      writeData( file, getPhone() );

   }

   // The method that actually writes the data to the file
   private void writeData( RandomAccessFile f, String n) throws IOException {
      StringBuffer buf = null;

      if ( n != null )
         buf = new StringBuffer( n );
      else
         buf = new StringBuffer( 20 );
         buf.setLength( 20 );
         f.writeChars( buf.toString() );
   }

   // This pads the data so to make the data all the same size
   // we will go for a size of 20
   private String padData( RandomAccessFile f ) throws IOException {
      char data[] = new char[ 20 ], temp;

      for ( int i = 0; i < data.length; i++ ) {
         temp = f.readChar();
         data[ i ] = temp;
      }

      return new String ( data ).replace( '\0', ' ' );
   }

   // This method hard codes the value for the size of the record
   // which is 20
   public static int size() { return 20; }

   // The get and set methods
   public void setName(String n) { name = n; }

   public void setPhone(String p) { phone = p; }

   public String getName() { return name; }

   public String getPhone() { return phone; }
}

File Class

The File class is useful for obtaining information about files and directories. Objects of class file do not actually open a file or provide any file-processing capabilities. The file class can provide the following and a lot more.

There are many methods in the File class, i have listed some of the more commonly used ones

canRead() Returns true if the file is readable, false otherwise
canWrite() Returns true if the file is writeable, false otherwise
delete() Delete a file or directory
exists() Returns true if the file exists, false otherwise
isFile() Returns true if the file is a file, false otherwise
isDirectory() Returns true if the file is a directory, false otherwise
isAbsolute() Returns true if the file is the absolute path to the file, false otherwise
getAbsolutePath() Returns a String with the absolute path of the file or directory
getName() Returns a String with the name of the file or directory
getParent() Returns a String with the parent directory of the file or directory
length() Returns the length of the file in bytes (long).
lastModified() Returns a platform-dependent representation of the time when the file or directory was modified (long)
list() Returns an array String representing the contents of a directory.
mkdir() Creates a directory
mkdirs() Create a directory, including any necessary but nonexistent parent directories
renameTo() Rename a file or directory
setReadOnly() Marks a file or directory as read-only
Examples
File example import java.io.*;
import java.util.Date;

public class fileTest {
   public static void main(String[] args) {

      File f = new File("d:\\java\\fileTest\\classes\\payroll.doc");
      File test1 = new File("d:\\java\\fileTest\\classes\\test1.doc");
      File test2 = new File("d:\\java\\fileTest\\classes\\test2.doc");
      File dir1 = new File("d:\\java\\fileTest\\classes\\payroll\\employee");
      File dir2 = new File("d:\\java\\fileTest\\classes\\payroll\\employee\\HR");
      File dir3 = new File("d:\\java\\fileTest\\classes\\payroll\\employee\\Sales");

      try {
         // Create a new file and rename
         test1.createNewFile();
         test1.renameTo(test2);

         // set test2 file as Read-Only
         test2.setReadOnly();

         // Create some directories then delete one
         dir1.mkdirs();             // used for creating multiple directories
         dir2.mkdir();
         dir3.mkdir();
         dir3.delete();

         // First let check that it exists
         if ( f.exists()) {
           // Let's test the object to see if its a file or directory
           if ( f.isFile() )
           {
              System.out.println("This file exists: " + f.getName());
              System.out.println("The absolute path of the file is: " + f.getAbsolutePath());
              System.out.println("getParent returns:" + f.getParent() + "\n");
              System.out.println("The canonical path of the file is: " + f.getCanonicalPath());

              System.out.println("Read the file:" + f.canRead());
              System.out.println("Write to the file:" + f.canWrite());

              System.out.println("File hidden:" + f.isHidden());
              long lm = f.lastModified();
              System.out.println("Last Modified:" + new Date(lm));

              System.out.println("Size of file: " + f.length()/1024 + "KB" );
           }
           else if (f.isDirectory()) {
                 System.out.println("This directory exists: " + f.getName());
                 System.out.println("The absolute path of the file is: " + f.getAbsolutePath());
           }
        } else {
             System.out.println("The file or directory does not exists: " + f.getName());
        }
      } catch (IOException e) {
           System.out.println("IOException caught" + e);
      }
   }
}