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, they can be categorized in the following

Byte Stream Classes
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.

Character Stream Classes
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

Reading Console Input

You should use a character-oriented stream to read in console input, this allows you to accept international characters, there are a number of ways to read console input the the most popular are below

Using BufferReader
public class ConsoleReader1 {

    public static void main(String[] args) {
        String name = "";

        System.out.println("Enter your name");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        try {
            name = br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Your name is: " + name);
    }
}
Using Scanner
public class ConsoleReader2 {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Enter your name: ");
        String name = scanner.nextLine();

        System.out.print("Enter your age: ");
        int age = scanner.nextInt();

        System.out.println("Your name is " + name + " and you are " + age + " year old");
    }
}
Using Console (now perferred)
public class ConsoleReader3 {

    public static void main(String[] args) {

        Console console = System.console();
        if (console == null) {
            System.out.println("No console: non-interactive mode!");
            System.exit(0);
        }

        System.out.print("Enter your username: ");
        String username = console.readLine();

        System.out.print("Enter your password: ");
        char[] password = console.readPassword();

        String passport = console.readLine("Enter your %d (th) passport number: ", 2);

        System.out.println("Your name is " + username + " and your password is " + password);
    }
}

Sequential File Access

The below example demonstrates how to write and read from a sequential file. I am using a few features not yet discussed but its easy to write and read back from a file in Java, I do cover the File class later in this section.

Sequential File Access
public class FileIO {

    public static void main(String[] args) {
        writeFile();
        readFile();
    }

    static void writeFile() {
        File file = new File("testfile.txt");
        String content = "This is the text content";

        try (FileOutputStream fop = new FileOutputStream(file)) { 				\\ using the new try-resources-close

            // if file doesn't exists, then create it
            if (!file.exists()) {
                file.createNewFile();
            }

            // get the content in bytes
            byte[] contentInBytes = content.getBytes();

            fop.write(contentInBytes);
            fop.flush();
            fop.close();

            System.out.println("Done");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static void readFile() {
        File file = new File("testfile.txt");

        try (FileInputStream fis = new FileInputStream(file)) { 				\\ using the new try-resources-close

            System.out.println("Total file size to read (in bytes) : "+ fis.available());

            int content;
            while ((content = fis.read()) != -1) {
                // convert to char and display it
                System.out.print((char) content);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

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);
      }
   }
}