Records and Objects

  1. A simple Record
  2. The Population Record
  3. A File Reading Object
  4. Creating a Binary File
  5. A BinaryFfile Manager
  6. Reading from a Binary File
  7. Writing to a Binary File


A simple Record : Ass2s2p1.java        

Revisit the text file of 1.3.4. and create a special Record class, called MyRecord.java:

// The "MyRecord" class.
public class MyRecord
{
    String code;
    String province;
    double year [] = new double [5];

    //constructor
    public MyRecord ()
    {
    }//end constructor
} // MyRecord class

Then insert and extra column containing the official 2-letter provincial codes ( use NU for Nunavut) and read the file with the following program (make sure both programs are saved in the same directory)

NF Newfoundland and Labrador     545.3     540.9     537.9     533.8     531.6
PE Prince Edward Island          136.9     137.8     138.3     138.9     139.9
NS Nova Scotia                   936.1     941.2     942.3     942.9     944.8
NB New Brunswick                 753.3     755.5     755.6     756       756.7
QC Quebec                        7323.6    7351.2    7381.8    7417.7    7455.2
ON Ontario                       11387.4   11527.9   11697.6   11894.9   12068.3
MB Manitoba                      1137.9    1142.5    1146.4    1149.1    1150.8
SK Saskatchewan                  1024.9    1025.6    1022      1017.1    1011.8
AB Alberta                       2906.8    2959.6    3009.9    3059.1    3113.6
BC British Columbia              3997.1    4028.3    4060.1    4101.6    4141.3
YK Yukon                         31.5      31.1      30.6      30.2      29.9
NT Northwest Territories         41.1      41        40.8      41.2      41.4 
NU Nunavut                       26.4      26.9      27.5      28.1      28.7

// The "Ass2s2p1" class.
import java.awt.*;
import java.io.*;
import hsa.TextConsole;

public class Ass2s2p1
{
    static TextConsole cs = new TextConsole ();
    static MyRecord list [] = new MyRecord [13]; //create a new array

    public static void main (String [] args) throws IOException
    {
     readText ("population.txt");
     cs.print ("enter a provincial code: ");
     String c = cs.readLine ();
     int location = -1;
     for (int i = 0 ; i < 13 ; i++)
         if (c.equalsIgnoreCase (list [i].code))
             location = i;
     if (location > -1)
     {
         cs.println("Population expressed in thousands");
         cs.println ("Province                      1998      1999      2000      2001      2002");
         cs.print (list [location].province, 25);
         for (int i = 0 ; i < 5 ; i++)
              cs.print (list [location].year [i], 10, 1);
         cs.println ();
     }
    } // main method


    public static void readText (String fileName) throws IOException
    {
     String line;
     FileReader f = new FileReader (fileName);
     BufferedReader input = new BufferedReader (f);
     MyRecord prov;
     for (int i = 0 ; i < 13 ; i++)
     {
         prov = new MyRecord (); // create space for a new record
         line = input.readLine ();
         prov.code = line.substring (0, 2);
         prov.province = line.substring (3, 30).trim ();
         prov.year [0] = Double.valueOf (line.substring (30, 40)).doubleValue ();
         prov.year [1] = Double.valueOf (line.substring (40, 50)).doubleValue ();
         prov.year [2] = Double.valueOf (line.substring (50, 60)).doubleValue ();
         prov.year [3] = Double.valueOf (line.substring (60, 70)).doubleValue ();
         prov.year [4] = Double.valueOf (line.substring (70)).doubleValue ();
         list [i] = prov;
     } // end i-loop


     input.close ();
    } // end readText
} // Ass2s2p1 class



The Population Record: Ass2s2p2.java        

It is bad OOP practice to let another program directly access one's data. It is standard practice to have methods deal with this. Create a new Record class called PopRecord, and add methods that will pass the information to and from the record, e.g.:

     public void putCode(String c)
     {
         code = c;
     }
     public String getCode(String c)
     {
        return code;
     }

Then modify problem 2.2.1 appropriately: Replace for example
prov.code= line.substring(0,2); by: prov.putCode(line.substring(0,2));

// The "Ass2s2p2" class.
import java.awt.*;
import java.io.*;
import hsa.TextConsole;

public class Ass2s2p2
{
    static TextConsole cs = new TextConsole ();
    static PopRecord list [] = new PopRecord [13]; //create a new array

    public static void main (String [] args) throws IOException
    {
     readText ("population.txt");
     cs.print ("enter a provincial code: ");
     String c = cs.readLine ();
     int location = -1;
     for (int i = 0 ; i < 13 ; i++)
         if (c.equalsIgnoreCase (list [i].getCode ()))
             location = i;
     if (location > -1)
     {
         cs.println ("Province                      1998      1999      2000      2001      2002");
         cs.print (list [location].getName (), 25);
         for (int i = 1998 ; i < 2003 ; i++)
          cs.print (list [location].getYear (i), 10, 1);
         cs.println ();
     }
    } // main method


    public static void readText (String fileName) throws IOException
    {
     String line;
     FileReader f = new FileReader (fileName);
     BufferedReader input = new BufferedReader (f);
     double pop;
     PopRecord prov;
     for (int i = 0 ; i < 13 ; i++)
     {
         prov = new PopRecord (); // create space for a new record
         line = input.readLine ()+"         ";
         prov.putCode (line.substring (0, 2));
         prov.putName (line.substring (3, 30).trim ());
         for (int yr = 0 ; yr < 5 ; yr++)
         {
          pop = Double.valueOf (line.substring (30 + yr * 10, 40 + yr * 10)).doubleValue ();
          prov.putYear (yr + 1997, pop);
         }
         list [i] = prov;
     } // end i-loop

     input.close ();
    } // end readText

} // Ass2s2p2 class



A File Reading Object: Ass2s2p3.java        

Convert the filereading method to a separate class also with constructor ReadProvPops( String filename) and one extra method: PopRecord getOne (int i) that will pass on to the main program the i-th record.

The ReadProvPops Class

This class will load the entire text file into an array of records, when instantiated.
The method PopRecord getOne(int i) will pass the ith record to the calling program.

// The "ReadProvPops" class.
import java.io.*;

public class ReadProvPops
{
    private PopRecord list []; 
    private final int FILELEN=13;

    public ReadProvPops (String fileName) throws IOException
    {
     list=new PopRecord [FILELEN];
     String line;
     FileReader f = new FileReader (fileName);
     BufferedReader input = new BufferedReader (f);
     double pop;
     PopRecord prov;
     for (int i = 0 ; i < FILELEN ; i++)
     {
         prov = new PopRecord (); // create space for a new record
         line = input.readLine () + "         ";
         prov.putCode (line.substring (0, 2));
         prov.putName (line.substring (3, 30).trim ());
         for (int yr = 0 ; yr < 5 ; yr++)
         {
          pop = Double.valueOf (line.substring (30 + yr * 10, 40 + yr * 10)).doubleValue ();
          prov.putYear (yr + 1997, pop);
         }
         list [i] = prov;
     } // end i-loop
     input.close ();
    } // end constructor


    public PopRecord getOne (int i)
    {
     if (i > -1 && i < FILELEN)
         return list [i];
     return null;
    }// end getOne
} // ReadProvPops class


The PopRecord Class

Each record has this format: a code, a name and 5 double type data items

// The "PopRecord" class.
public class PopRecord
{
    private String code;
    private String name;
    private double year [] = new double [5];

    //constructor
    public PopRecord ()
    {
    }


    public void putCode (String c)
    {
     code = c;
    }


    public void putName (String n)
    {
     name = n;
    }


    public void putYear (int y, double p)
    {
     if (y > 1997 & y < 2003)
         year [y - 1998] = p;
    }


    public String getCode ()
    {
     return code;
    }


    public String getName ()
    {
     return name;
    }


    public double getYear (int y)
    {
     if (y > 1997 & y < 2003)
         return year [y - 1998];
     return 0;
    }
} // PopRecord class

The Calling Program

// The "Ass2s2p3" class.
import java.awt.*;
import java.io.*;
import hsa.TextConsole;

public class Ass2s2p3
{
    static TextConsole cs = new TextConsole ();
    static PopRecord prov = new PopRecord ();
    static ReadProvPops file;

    public static void main (String [] args) throws IOException
    {
     file = new ReadProvPops ("population.txt");
     cs.print ("enter a provincial code: (enter an invalid one to stop) ");
     String c = cs.readLine ();
     int here = locationFromCode (c);
     while (here != -1)
     {
         prov = file.getOne (here);
         cs.println ("Province                      1998      1999      2000      2001      2002");
         cs.print (prov.getName (), 25);
         for (int i = 1997 ; i < 2002 ; i++)
             cs.print (prov.getYear (i), 10, 1);
         cs.println ();
         cs.print ("enter a provincial code: (enter an invalid one to stop) ");
         c = cs.readLine ();
         here = locationFromCode (c);
     } //end while
     cs.println("bye");
    } // main method


    public static int locationFromCode (String code)
    {
     for (int i = 0 ; i < 13 ; i++)
         if (code.equalsIgnoreCase (file.getOne (i).getCode ()))
             return i;
     return -1;
    } //end locationFromCode
} // Ass2s2p3 class


Creating a Binary File: Ass2s2p4.java        

Write a program that will convert this text file into a binary file. Allocate 30 bytes for the city's name, and 8 each for the double year values for a total of 70 bytes each record. Use the length() method to find the total length of the file.

cities.txt to be converted to cities.dat

Toronto (ON)                  4586.7    4673.3    4773.6    4907      5029.9    
MontrÈal (QC)                 3423.9    3449.2    3479.4    3511.4    3548.8    
Vancouver (BC)                1998.4    2028.9    2060.7    2099.4    2136.2    
Ottawa-Hull (ON-QC)           1055.6    1069      1087.3    1108.5    1128.9    
Calgary (AB)                  903.1     926.2     947.9     969.6     993.2     
Edmonton (AB)                 914.4     928.1     941.8     954.1     967.2     
QuÈbec (QC)                   686.6     688.5     690.8     694       697.8     
Winnipeg (MB)                 677.8     679.9     682.4     684.3     685.5     
Hamilton (ON)                 657.8     664.8     672.1     680       686.9     
London (ON)                   416       418.6     422.3     425.2     427.3     
Kitchener (ON)                408.5     415.8     423.8     431.2     438       
St. Catharines-Niagara (ON)   387.5     388.8     390.8     391.9     392.3     
Halifax (NS)                  348.9     353.4     356       359.1     363.2     
Victoria (BC)                 316.8     316.9     317.1     319.4     322.1     
Windsor (ON)                  295.9     300.6     307       313.7     319.9     
Oshawa (ON)                   287.5     292.5     298.9     304.6     310       
Saskatoon (SK)                229.5     230.7     231       231.5     231.8     
Regina (SK)                   199.2     199.8     199.3     198.3     197       
St. John's (NF)               175.2     175.4     176.2     176.4     177.2     
Chicoutimi-JonquiËre (QC)     162.6     161.8     160.3     158.8     156.9     
Sudbury (ON)                  162       159.3     157.7     157       155.9     
Sherbrooke (QC)               152.3     152.7     153.8     155       156.5     
Trois-RiviËres (QC)           141.9     141.8     141.6     141.2     141.4     
Thunder Bay (ON)              127.5     126.9     125.9     125.7     125.1     
Saint John (NB)               127.5     127.7     127.7     127.3     127       

// The "Ass2s2p4" class.
import java.awt.*;
import java.io.*;
import hsa.TextConsole;


public class Ass2s2p4
{
    static TextConsole cs = new TextConsole ();
    static PopRecord prov = new PopRecord ();
    static ReadProvPops file;

    public static void main (String [] args) throws IOException
    {
     file = new ReadProvPops ("population.txt");
     RandomAccessFile f = new RandomAccessFile ("population.dat", "rw");
     for (int i = 0 ; i < 13 ; i++)
     {
         prov = file.getOne (i);
         byte codeBytes [] = new byte [2];
         prov.getCode ().getBytes (0, 2, codeBytes, 0);
         f.write (codeBytes);
         byte nameBytes [] = new byte [28];
         // add an extra 28 spaces to assure that there are at least 28
         String temp = prov.getName () + "                            ";
         temp.getBytes (0, 28, nameBytes, 0);
         f.write (nameBytes);
         for (int yr = 1998 ; yr < 2003 ; yr++)
             f.writeDouble (prov.getYear (yr));
     } //end for each record

     cs.println ("there are a total of " + f.length () + " bytes in the file");
     cs.println ("13 records each holding " + f.length () / 13 + " bytes each");
     f.close ();
    } // main method

} // Ass2s2p4 class




A Binary File Manager: CityFile.java        

When instantiated, the CityFile will open the file, and makes available 3 methods:

  • CityRecord getCityRecord (int i) which returns the ith record
  • void putCityRecord (CityRecord rec, int i) which saves a record in the ith place
  • long getLength () which returns the total number of records in the file.

// The "CityFile" class.
import java.io.*;

public class CityFile
{
    private RandomAccessFile f;
    private CityRecord r;
    private long len = 0;
    private long fileLen = 0;
    private long current = 0;
    private String fileName;

    public CityFile (String fileName) throws IOException
    {
     this.fileName = fileName;
     f = new RandomAccessFile (fileName, "rw");
     fileLen = f.length ();
     len = fileLen / 70;
    } // end constructor


    public CityRecord getCityRecord (int i) throws IOException
    {
     CityRecord r = new CityRecord ();
     current = i;
     if (i > len)
         current = len - 1;
     if (i < 0)
         current = 0;
     f.seek ((long) current * 70);
     byte codeBytes [] = new byte [30];
     f.readFully (codeBytes);
     String name = new String (codeBytes, 0).trim ();
     r.putName (name);
     for (int yr = 1998 ; yr < 2003 ; yr++)
     {
         double tempDouble = f.readDouble ();
         r.putYear (yr, tempDouble);
     }
     return r;
    } //end getCityRecord


    public void putCityRecord (CityRecord rec, int i) throws IOException
    {
     current = i;
     if (i > len)
         current = len;
     if (i < 0)
         current = 0;
     f.seek ((long) current * 70);
     String name = rec.getName () + "                              ";
     f.writeBytes (name.substring (0, 30));
     for (int yr = 1998 ; yr < 2003 ; yr++)
         f.writeDouble (rec.getYear (yr));
     f.close ();
     f = new RandomAccessFile (fileName, "rw");
     len = f.length () / 70;
    } //end putCityRecord


    public long getLength ()
    {
     return len;
    }
} // CityFile class

Reading from a Binary File: Ass2s2p5.java        

Create a program that will let you read from from the binary file (see cities.dat below) and print out any record, given a record number: 1 for Toronto, 6 for Edmonton, etc.

// The "Ass2s2p5" class.
import java.awt.*;
import java.io.*;
import hsa.TextConsole;

public class Ass2s2p5
{
    static TextConsole cs = new TextConsole ();
    static CityRecord cityRec = new CityRecord ();
    static CityFile f; 

    public static void main (String [] args)throws IOException
    {
    f= new CityFile ("cities.dat");
     long len = f.getLength ();
     cs.print ("enter a number between 0 and " + (len - 1) + " (negative to stop): ");
     int location = cs.readInt ();
     while (location >= 0)
     {
         cityRec = f.getCityRecord (location);
         cs.println ("Population expressed in thousands");
         cs.println ("City                               1998      1999      2000      2001      2002");
         cs.print (cityRec.getName(), 30);
         for (int y = 1998 ; y < 2003 ; y++)
              cs.print (cityRec.getYear (y), 10, 1);
         cs.println ("\n");
         cs.print ("enter a number between 0 and " + (len - 1) + " (negative to stop): ");
         location = cs.readInt ();
     }
     cs.println("bye!");
    } // main method
    
} // Ass2s2p5 class





Writing to a Binary File: Ass2s2p8.java        

Write a program that lets you add data to the Cities.dat file. Then add the populations of:
       Iqaluit (Nunavut), population: 4.4, 4.6, 4.8, 5.0, 5.2
       Yellowknife (NT) population: 17.2, 17.1, 16.9 , 16.7 , 16.5
       Charlottetown (PE) population: 32.5, 32.4, 32.3. 32.2, 32.2


// The "Ass2s2p8" class.
import java.awt.*;
import java.io.*;
import hsa.Console;

public class Ass2s2p8
{
    static Console cs = new Console ();
    static CityRecord cityRec = new CityRecord ();
    static CityFile f;

    public static void main (String [] args) throws IOException
    {
     f = new CityFile ("cities.dat");
     long len = f.getLength ();
     cs.print ("Enter the name of a new city or \"stop\" to stop: ");
     String name = cs.readLine ();
     while (!name.equalsIgnoreCase ("stop"))
     {
         cityRec.putName (name);
         for (int y = 1998 ; y << 2003 ; y++)
         {
          cs.print ("enter the population in " + y + "in thousands: ");
          double pop = cs.readDouble ();
          cityRec.putYear (y, pop);
         }
         cs.print ("\nis this correct? (y or n): ");
         String c = cs.readString ();
         if (c.charAt (0) == 'y' || c.charAt (0) == 'Y')
         {
          f.putCityRecord (cityRec, (int) f.getLength ());
          cs.println ("successfully recorded");
         }
         else
          cs.println ("record data ignored");
         cs.print ("Enter the name of a new city or \"stop\" to stop: ");
         name = cs.readLine ();

     }
     cs.println ("bye!");
    } // main method
} // Ass2s2p8 class







Sponsored by ECOO and SIG-Computer Science

.