On completion of this section you will be familiar with:
All of our data up to now was fairly nebulous in that it existed only while our programmes were running. Once those programmes stopped running the memory allocated to them and their data was wiped clean and thus the data was irretrievably lost. Up to now it did not matter as the amount of data was so small and we simply concentrated on the processing of that data and not on storing large amounts of it.
In a real life situation if we were processing a payroll we would need to keep permanent records of the results since we would need to supply yearly statements both to the inland revenue and to the individual employees. The way of doing this is to store data in a disk file. In this chapter we shall be looking at one of the simplest forms of data storage – a random access text file
If we were to store the following data in a text file: hours worked, hourly rate and superannuation code, then the first thing we need to do is decide on what data types we are going to use for each item. If we decide on the type double for the hours and rate and the type int for the superannuation code, then, since the type double is 8 bytes long and the type int is 4 bytes long. Thus for each employee we would use 8 + 8 + 4 = 20 bytes to store the data on a disk file. The structure would look as follows:
|
hours |
rate |
Super code |
|||||||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The example above shows a file structure which has three fields or columns as they are called in some texts. The first field, named hours is 8 bytes long, the second field, rate, is also 8 bytes long while the last field super code is 4 bytes long.
If we begin with the above structure then we need to abide by it through our application. The reason is that since the length of each record is 20 bytes then we can tell the system to go to any record in the file directly rather than having to search from the beginning of the file. In most file systems positioning of the bytes starts at zero and thus in the above structure we are looking at bytes 0 – 19. The records themselves start counting at 0 as well. Thus if we wanted the fifth record we would simply multiply 4 by 20 and thus we would know to go directly to bytes position 80 to pick up the beginning of the fifth record. Since we know that that record, like all of the other records are structured as 8, 8, 4, then once we got to position 80 we would read 8 bytes, followed by another 8 bytes and finally read 4 bytes. This would incidentally position us at the beginning of the sixth record. Since a file generally has more than one record the illustration below is more descriptive of a real file than that above.
|
hours |
rate |
Super code |
|||||||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Let us now look at an application that uses a file structure similar to the above to store data a bout a series of payroll data.
To begin with we have an updated version of our keyboard reader. In this case we have created a class, an object of which can read the keyboard data and has three methods for returning the data as String, double or int.
Listing 13.1
|
1 |
import
java.io.IOException; |
|
2 |
public class
ReadKeyboard |
|
3 |
{ |
|
4 |
byte[] input; |
|
5 |
boolean dataRead; |
|
6 |
public ReadKeyboard(){}; |
|
7 |
String inputString; |
|
8 |
public ReadKeyboard(String prompt) |
|
9 |
{ |
|
10 |
input = new byte[20]; |
|
11 |
try |
|
12 |
{ |
|
13 |
System.out.println(prompt); |
|
14 |
System.in.read(input); |
|
15 |
inputString = new String(input); |
|
16 |
dataRead=true; |
|
17 |
} |
|
18 |
catch(IOException e) |
|
19 |
{ |
|
20 |
dataRead=false; |
|
21 |
} |
|
22 |
} |
|
23 |
public String stringValue() |
|
24 |
{ |
|
25 |
return inputString; |
|
26 |
} |
|
27 |
public int intValue() |
|
28 |
{ |
|
29 |
try |
|
30 |
{ |
|
31 |
return
Integer.parseInt(inputString.trim()); |
|
32 |
} |
|
33 |
catch(NumberFormatException n) |
|
34 |
{ |
|
35 |
return Integer.MIN_VALUE; |
|
36 |
} |
|
37 |
} |
|
38 |
public double doubleValue() |
|
39 |
{ |
|
40 |
try |
|
41 |
{ |
|
42 |
return
Double.parseDouble(inputString.trim()); |
|
43 |
} |
|
44 |
catch(NumberFormatException n) |
|
45 |
{ |
|
46 |
return Double.NaN; |
|
47 |
} |
|
48 |
} |
|
49 |
} |
The constructor of this class, which spans lines 8 – 22, accepts the user’s prompt. It has its own byte array at line 10 into which it accepts the keyboard input. In a try block at lines 11 – 17 it displays the prompt, reads the keyboard and converts the data to a String which is stored in the class variable inputString. Finally it sets the flag dataRead to true. If an exception has been thrown then at line 20 the flag is set to false.
The method stringValue simply returns the value of inputString directly.
The method intValue returns the integer value of the same String, and in case an exception is thrown it sends the MIN_VALUE of the Integer class.
The doubleValue method works in a similar manner for that data type.
Finally let us look at the class for processing an actual file in Listing 13.2 below.
Listing 13.2
|
1 |
import
java.io.FileInputStream; |
|
2 |
import
java.io.FileOutputStream; |
|
3 |
import
java.io.IOException; |
|
4 |
import java.io.FileNotFoundException; |
|
5 |
import
java.io.DataInputStream; |
|
6 |
import
java.io.DataOutputStream; |
|
7 |
public class
FileProcessing |
|
8 |
{ |
|
9 |
public void writeToFile(String fileName) |
|
10 |
{ |
|
11 |
try |
|
12 |
{ |
|
13 |
double value; |
|
14 |
int value1; |
|
15 |
FileOutputStream fo = new
FileOutputStream(fileName); |
|
16 |
DataOutputStream dos = new
DataOutputStream(fo); |
|
17 |
value = new ReadKeyboard("Enter
Hours").doubleValue(); |
|
18 |
while(!Double.isNaN(value)) |
|
19 |
{ |
|
20 |
dos.writeDouble(value); |
|
21 |
value = new ReadKeyboard("Enter
Rate").doubleValue(); |
|
22 |
dos.writeDouble(value); |
|
23 |
value1 = new ReadKeyboard("Enter
Super code").intValue(); |
|
24 |
dos.writeInt(value1); |
|
25 |
value = new ReadKeyboard("Enter
Hours").doubleValue(); |
|
26 |
} |
|
27 |
dos.close(); |
|
28 |
fo.close(); |
|
29 |
} |
|
30 |
catch(FileNotFoundException fnf){} |
|
31 |
catch(IOException ioe){} |
|
32 |
} |
|
33 |
public void readFromFile(String fileName) |
|
34 |
{ |
|
35 |
Pay p; |
|
36 |
double hours, rate; |
|
37 |
int superCode; |
|
38 |
try |
|
39 |
{ |
|
40 |
FileInputStream fis = new
FileInputStream(fileName); |
|
41 |
DataInputStream dis = new
DataInputStream(fis); |
|
42 |
while(dis.available()>0) |
|
43 |
{ |
|
44 |
hours = dis.readDouble(); |
|
45 |
rate = dis.readDouble(); |
|
46 |
superCode = dis.readInt(); |
|
47 |
p = new Pay(hours,rate,superCode); |
|
48 |
p.calculate(); |
|
49 |
System.out.println(p.toString()); |
|
50 |
} |
|
51 |
dis.close(); |
|
52 |
fis.close(); |
|
53 |
} |
|
54 |
catch(FileNotFoundException fnf) |
|
55 |
{} |
|
56 |
catch(IOException io){} |
|
57 |
} |
|
58 |
} |
The class simply consists of two methods writeToFile and readFromFile. In the first method, which spans lines 9 – 32, most of the processing takes place inside a try block. At lines 13 and 14 we declare two variables that we shall use to store the keyboard data before storing it in the file.
At line 15 we create an object of the class FileOutputStream and pass to its constructor the name of the file that we are to write data to. FileOutputStream can be regarded as a road or channel that has been opened between the computer’s memory and the disk on which the data is to be stored. It does not, in our case, deal with any data types or structures. Instead it simply looks after the data transfer protocols between the memory and the file. In order to take care of the actual data itself we create an object of the class DataOutputStream at line 16. We pass to this object’s constructor a pointer to the FileOutputStream object we created earlier. This last object we created will take care of the data structures.
Next we commence reading the data from the keyboard. The sequence we shall read it is hours, rate and super code. A while loop will be used to control the keyboard entry. Once a blank is received from the keyboard the loop will terminate. The first two double values received from the keyboard are written to the file at lines 20 and 22. The method writeDouble simply writes 4 bytes of data to the file, which are encoded as the type double. Similarly, at line 24, writeInt writes an integer value to the file. Thus by line 24 two doubles and an int are written to the file. On the next iteration of the loop a similar sequence will be written until the user eventually terminates the loop. Once this occurs the two streams are closed at lines 27 and 28.
The method readFromFile spans lines 33 – 58. The method’s variables consist of a pointer to the class Pay, and the variables hours, rate and superCode. Again all of the processing takes place inside a try block. The classes FileInputStream and DataInputStream are very similar to their equivalents above except that they are involved with data going from a disk file to the memory. Thus their methods are read methods instead of write methods.
A method that belongs to the class DataInputStream is the method available. This method needs a little explanation. When we open a text file for reading a file pointer is positioned at the start of the file and as we read data from that file the pointer moves along with us. The method available simply counts the number of bytes between the current position of the file pointer and the end of the file. We use this method in our application to prevent us from trying to read beyond the end of the file. Thus at line 41 once the DataInputStream object is created the file pointer is positioned at the start of the file. Unless its an empty file the condition at line 42 will be false and thus at lines 44 – 46 two double and one int values re read from the file. (This of course will cause the pointer to move along by 20 bytes towards the end of the file. Once the three values are read an object of the class Pay is created and the three values that have been read are passed to its constructor. Its calculate method is called at line 48 and the result of the calculation is displayed at line 49. The loop iterates once more to read the next record where the same processing occurs.
Finally the code below starts off the application by first creating an object of FileProcessing and then calling its two methods.
Listing 13.3
|
1 |
public class Untitled1 |
|
2 |
{ |
|
3 |
public static void main(String[] args) |
|
4 |
{ |
|
5 |
FileProcessing fp = new FileProcessing(); |
|
6 |
fp.writeToFile("Employees"); |
|
7 |
fp.readFromFile("Employees"); |
|
8 |
} |
|
9 |
} |
Copy the above code into your computer, then compile and run it. Once it is running satisfactorily, alter the processing so that once the hours and rate are received in the method writeToFile the gross, tax, superannuation and nett are calculate immediately and stored in the file along with the rest of the data. Thus in the method readFromFile no processing occurs. It simply reads the data items from the file and prints the results.
A text file is a structure which consists of records, which themselves are made up of fields. Each record is of a fixed size so that the position of each record can be easily calculated by the system for fast retrieval of data.
In Java we use a combination of objects of the classes FileOutputStream and DataOutputStream to write data to the file. The former establishes connection between the memory and the disk while the latter takes care of the data structure that is to be transferred.
To read data from a file we use combination of objects of the classes FileInputStream and DataInputStream.
1. What is a record?
2. What is a field?
3. Give alternative names for record and field.
4. Explain the use of objects of the classes FileInputStream and DataInputStream.
5. What are the similarities and differences between FileInputStream and FileOutputStream?
6. Discuss the methods writeDouble and writeInt. These are closely matched by two other methods. What are they?
Modify Exercise 12.1 so that when the user input is received the calculations
for GST, price including GST and total charged to customer are stored in a
file instead of being displayed on the screen. There must be a method in your
class for reading the data from the file and displaying it. This method will
not perform any calculations.
Use listings 13.1, 13.2 and 13.3 above as models or templates of how to create
the new application.