Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Week 7: (Horstman ch.15, 16, Deite&Deitel ch.16 and Lewis&Loftus ch.8.4) Exception Handling We encountered already exception handling in ITEC1620, when reading from an external file. import java.util.Scanner; import java.io.*; public class ExampleFile{ public static void main(String[] args) throws IOException{ Scanner fileInput = new Scanner(new File("input.txt")); PrintStream fileOutput = new PrintStream(new File("output.txt")); int sum = 0; for( ; fileInput.hasNextInt(); ){ sum = sum + fileInput.nextInt(); } fileOutput.println("The sum is: " + sum); fileInput.close(); fileOutput.close(); } } where the content of the file input.txt was: 1 2 3 4 5 6 7 IOException is a case of checked exception and the compiler checks that the programmer does not ignore it. A checked exception describes a problem that is likely to occur no matter how careful you are. Input/output and systems operations are typical cases of checked exceptions. Your program has to throw all checked exceptions. On the other hand errors that you make as a programmer are called unchecked exceptions and you are not forced to deal with them. Such errors are a division by zero, referring to an element outside the size of an array. Good programming requires that you deal with these exceptions too but if you do not your program will still compile. Every exception should be handled somewhere in your program. If an exception has no handler an error message is printed and your program terminates. This is not what a professionally written program should do. You can install an exception handler with the try/catch statement. import java.util.Scanner; import java.io.*; public class ExampleFile1{ public static void main(String[] args) { int sum = 0; Scanner fileInput=null; PrintStream fileOutput=null; try { fileInput = new Scanner(new File("input.txt")); fileOutput = new PrintStream (new File("output.txt")); for( ; fileInput.hasNextLine(); ){ String s = fileInput.nextLine(); sum = sum + Integer.parseInt(s); } } catch (IOException exception) { System.out.println("File not found"); } catch (NumberFormatException exception) { System.out.println("Input was not a number"); } finally{ fileOutput.println("The sum is: " + sum); fileInput.close(); fileOutput.close(); } } } The try block contains several sources of error and one can use several catch blocks to address them. The File class constructor might not find the input file and will throw a FileNotFoundException (which is a possible IOException). In the catch corresponding to that exception there is clear message. The second source of exception is Integer.parseInt(s) in cases when the input file contains a non-numeric input. This is an unchecked RuntimeException, which is caught to create a clear error message. Independent of whether an exception was made or not one must close the files and these statements are put in the finally block. Files and I/O Streams Storage of data in variables and arrays is temporary – the data is lost when the program terminates. You have to use files for long-term retention of data. Like programs, files can be stored on storage devices such as magnetic or optical disks. Data Hierarchy. The smallest data item is the bit. The next is the character and Java uses Unicode characters built of 2 bytes each (ie 16 bits). Some files work with characters: text files, image files, audio files. But in many industries, such as banking, data is further organized in fields and records. A field is a sequence of characters that convey some meaning. For instance a name can be a field. A record contains several fields. For instance an employee record in a payroll system has: employee number, name, pay rate, year-to-date earnings, taxes withheld (the first 2 fields are Strings, the next 3 are doubles). To facilitate the retrieval of specific records one field of the record is chosen as record key. In the above example employee number is most suitable to be a record key. The most common organization of a file of records is the randon-access file, in which records are stored in order by the record key field. Most businesses have many different files: payroll, accounts receivable, accounts payable, inventory etc. Often a group of files is called a database. Files and Streams. Java views each file either as a stream of bytes or characters. Its package java.io contains classes FileInputStream and FileOutputStream for streams of bytes and FileReader and FileWriter for streams of characters. Reading a Text File. The easiest scenario is using a text file created with Notepad or any other text editor. In this case one uses the FileReader and BufferedReader classes. The second class filters data by buffering it into more accessible units. It uses the method readLine() to obtain a String which is then broken into components with a StringTokenizer object. The latest versions of JDK offered in addition classes Scanner and File, which can be used to replace BufferedReader and FileReader, respectively. import java.util.StringTokenizer; import java.io.*; import java.text.DecimalFormat; class InventoryItem { private String name; private int units; // number of available units of this item private float price; // price per unit of this item private DecimalFormat fmt; public InventoryItem (String itemName, int numUnits, float cost) { name = itemName; units = numUnits; price = cost; fmt = new DecimalFormat ("0.##"); } public String toString() { return name + ":\t" + units + " at " + price + " = " + fmt.format ((units * price)); } } public class Inventory{ //----------------------------------------------------------------- // Reads data about a store inventory from an input file, // creating an array of InventoryItem objects, then prints them. //----------------------------------------------------------------public static void main (String[] args) { final int MAX = 100; InventoryItem[] items = new InventoryItem[MAX]; StringTokenizer tokenizer; String line, name, file="inventory.dat"; int units, count = 0; float price; try { BufferedReader inFile = new BufferedReader (new FileReader (file)); line = inFile.readLine(); while (line != null) { tokenizer = new StringTokenizer (line); name = tokenizer.nextToken(); try { units = Integer.parseInt (tokenizer.nextToken()); price = Float.parseFloat (tokenizer.nextToken()); items[count++] = new InventoryItem (name, units, price); } catch (NumberFormatException exception) { System.out.println ("Error in input. Line ignored:"); System.out.println (line); } line = inFile.readLine(); } inFile.close(); for (int scan = 0; scan < count; scan++) System.out.println (items[scan]); } catch (FileNotFoundException exception) { System.out.println ("The file " + file + " was not found."); } catch (IOException exception) { System.out.println (exception); } } } The input file is shown below: Widget 14 3.35 Spoke 132 0.32 Wrap 58 1.92 Thing 28 4.17 Brace 25 1.75 Clip 409 0.12 Cog 142 2.08 Writing a Text File. Writing output to a text file requires the use of classes FileWriter, BufferedWriter and PrintWriter. (or in the latest versions of JDK classes File and PrintWriter). In the example below one computes random values which are then printed to an external file (the file is created by this program). import java.io.*; public class TestData { //----------------------------------------------------------------// Creates a file of test data that consists of ten lines each // containing ten integer values in the range 0 to 99. //----------------------------------------------------------------public static void main (String[] args) throws IOException { final int MAX = 10; int value; String file = "test.dat"; PrintWriter outFile = new PrintWriter (new BufferedWriter (new FileWriter (file))); for (int line=1; line <= MAX; line++) { for (int num=1; num <= MAX; num++) { value = (int) (Math.random() * 100); outFile.print (value + " "); } outFile.println (); } outFile.close(); System.out.println ("Output file has been created: " + file); } } Sequential and Random-Access Files What if we want to store complex objects into files ? These objects would correspond to records in a database. Persistence is the concept that an object can “live” outside the program that created it. Files do not have a record structure, but programs can impose a record structure on a file. Java contains a mechanism called object serialization, which transforms an object into a sequence of bytes. Any object that we want to serialize must implement the interface Serializable. To serialize one uses the method writeObject() of the class ObjectOutputStream, which works in conjunction with the class FileOutputStream. Java allows for two types of records-based files to be created. The sequential files are like an oldfashioned music tape, where records of various length are in a sequence. One always have to start from the beginning of the tape to reach anywhere inside the tape. Random-access files are similar to a CD player. One can jump to a certain record if one knows its address (index). Obviously random-access files are more convenient to operate with. They are easier to update and easier to program. We shall restrict our discussion to random-access files. One more observation: like the sequential files, random-access files cannot be read with a text editor, as they are byte and not character-based. To read it and display each record one needs the following Java program. Random Access Files. For quick access to a certain record one has to use random-access files. The price for having this feature is that the file will be with fixed-length records. The structure of the file has to be decided before the loading of data. The following two programs will do that task. The first program will define a class that is used for reading and writing records to a random-access file. The second program will create a file with blank records. // Record class for the RandomAccessFile programs. import java.io.*; public class Record { int account; String lastName; String firstName; double balance; // Read a record from the specified RandomAccessFile public void read( RandomAccessFile file ) throws IOException { account = file.readInt(); byte b1[] = new byte[ 15 ]; file.readFully( b1 ); firstName = new String( b1, 0 ); firstName=firstName.trim(); byte b2[] = new byte[ 15 ]; file.readFully( b2 ); lastName = new String( b2, 0 ); lastName=lastName.trim(); balance = file.readDouble(); } // Write a record to the specified RandomAccessFile public void write( RandomAccessFile file ) throws IOException { file.writeInt( account ); byte b1[] = new byte[ 15 ]; if ( firstName != null ) firstName.getBytes( 0, firstName.length(), b1, 0 ); file.write( b1 ); byte b2[] = new byte[ 15 ]; if ( lastName != null ) lastName.getBytes( 0, lastName.length(), b2, 0 ); file.write( b2 ); file.writeDouble( balance ); } // NOTE: This method contains a hard coded value for the size of a record of information. public int size() { return 42; } } //========================================================== // This program creates a random access file sequentially by writing 100 empty records to disk. import java.io.*; public class CreateRandFile { private Record blank; RandomAccessFile file; public CreateRandFile() { blank = new Record(); try { file = new RandomAccessFile( "credit.dat", "rw" ); } catch( IOException e ) { System.err.println( "File not opened properly\n" + e.toString() ); System.exit( 1 ); } } public void create() { try { for ( int i = 0; i < 100; i++ ) blank.write( file ); } catch ( IOException e ) { System.err.println( e.toString() ); } } public static void main( String args[] ) { CreateRandFile accounts = new CreateRandFile(); accounts.create(); } } Having the empty records on the disk we can add records to the file by using the RandomAccessFile method seek() to determine the location to be stored. // This program uses TextFields to get information from the // user at the keyboard and writes the information to a random access file. import java.io.*; import java.awt.*; import javax.swing.*; import java.awt.event.*; public class WriteRandFile extends JFrame { // Application window components JTextField acct, // where user enters account number fName, // where user enters first name lName, // where user enters last name bal; // where user enters balance JButton enter, // send record to file done; // quit program JLabel acctLabel, // account label fNameLabel, // first name label lNameLabel, // last name label balLabel; // balance label RandomAccessFile output; // file for output Record data; public WriteRandFile() { super( "Write to random access file" ); data = new Record(); try { output = new RandomAccessFile( "credit.dat", "rw" ); } catch ( IOException e ) { System.err.println( e.toString() ); System.exit( 1 ); } Container c = getContentPane(); c.setLayout( new GridLayout( 5, 2 ) ); acct = new JTextField( 20 ); acctLabel = new JLabel( "Account Number" ); fName = new JTextField( 20 ); fNameLabel = new JLabel( "First Name" ); lName = new JTextField( 20 ); lNameLabel = new JLabel( "Last Name" ); bal = new JTextField( 20 ); balLabel = new JLabel( "Balance" ); enter = new JButton( "Enter" ); done = new JButton( "Done" ); c.add( acctLabel ); // add label c.add( acct ); // add TextField c.add( fNameLabel ); // add label c.add( fName ); // add TextField c.add( lNameLabel ); // add label c.add( lName ); // add TextField c.add( balLabel ); // add label c.add( bal ); // add TextField c.add( enter ); // add button c.add( done ); // add button done.addActionListener( new ActionListener () { public void actionPerformed( ActionEvent event ){ cleanup(); // write data, close file, etc. hide(); dispose(); // release system resources System.exit( 0 ); }} ); enter.addActionListener( new ActionListener () { public void actionPerformed( ActionEvent event ){ addRecord(); }} ); setSize( 300, 150 ); setVisible( true ); } public void addRecord() { int acctNum = 0; Double d; acctNum = ( new Integer( acct.getText() ) ).intValue(); // output the values to the file try { if ( acctNum > 0 && acctNum <= 100 ) { data.account = acctNum; data.firstName = fName.getText(); data.lastName = lName.getText(); d = new Double ( bal.getText() ); data.balance = d.doubleValue(); output.seek( (long) ( acctNum-1 ) * data.size() ); data.write( output ); // clear the TextFields acct.setText( "" ); fName.setText( "" ); lName.setText( "" ); bal.setText( "" ); } else { acct.setText( "Enter valid account (1-100)" ); acct.selectAll(); } } catch ( IOException e ) { System.err.println( "Error during write to file\n" + e.toString() ); System.exit( 1 ); } } public void cleanup() { if ( !acct.getText().equals("") ) addRecord(); try { output.close(); } catch ( IOException e ) { System.err.println( "File not closed properly\n" + e.toString() ); System.exit( 1 ); } } public static void main( String args[] ) { WriteRandFile accounts = new WriteRandFile(); } } The following program opens a random-access file and displays only the records with data. // This program uses TextFields to get information from the // user at the keyboard and writes the information to a random access file. import java.io.*; import java.awt.*; import javax.swing.*; import java.awt.event.*; public class ReadRandFile extends JFrame { // Application window components JTextField acct, // where user enters account number fName, // where user enters first name lName, // where user enters last name bal; // where user enters balance JButton next, // send record to file done; // quit program JLabel acctLabel, // account label fNameLabel, // first name label lNameLabel, // last name label balLabel; // balance label RandomAccessFile input; // file for output Record data; boolean moreRecords=true; public ReadRandFile() { super( "Write to random access file" ); data = new Record(); try { input = new RandomAccessFile( "credit.dat", "r" ); } catch ( IOException e ) { System.err.println( e.toString() ); System.exit( 1 ); } Container c = getContentPane(); c.setLayout( new GridLayout( 5, 2 ) ); acct = new JTextField( 20 ); acctLabel = new JLabel( "Account Number" ); fName = new JTextField( 20 ); fNameLabel = new JLabel( "First Name" ); lName = new JTextField( 20 ); lNameLabel = new JLabel( "Last Name" ); bal = new JTextField( 20 ); balLabel = new JLabel( "Balance" ); next = new JButton( "Next" ); done = new JButton( "Done" ); c.add( acctLabel ); // add label c.add( acct ); // add TextField c.add( fNameLabel ); // add label c.add( fName ); // add TextField c.add( lNameLabel ); // add label c.add( lName ); // add TextField c.add( balLabel ); // add label c.add( bal ); // add TextField c.add( next ); // add button c.add( done ); // add button done.addActionListener( new ActionListener () { public void actionPerformed( ActionEvent event ){ cleanup(); // write data, close file, etc. hide(); dispose(); // release system resources System.exit( 0 ); }} ); next.addActionListener( new ActionListener () { public void actionPerformed( ActionEvent event ){ readRecord(); }} ); setSize( 300, 150 ); setVisible( true ); } public void readRecord() { try { do { // loop over empty records data.read( input ); } while ( input.getFilePointer() < input.length() && data.account == 0 ); } catch( IOException e ) { moreRecords = false; } // transfer full record data into textfields if ( data.account != 0 ) { acct.setText( String.valueOf( data.account ) ); String fN=data.firstName; fName.setText( fN); String lN=data.lastName; lName.setText( lN ); bal.setText( String.valueOf( data.balance ) ); } } public void cleanup() { try { input.close(); } catch ( IOException e ) { System.err.println( e.toString() ); System.exit( 1 ); } } public static void main( String args[] ) { ReadRandFile accounts = new ReadRandFile(); } } The use of random-access files allows for the creation of a good transaction-processing program, able to perform quick retrieval and update of records. Such a program will contain part of the code discussed in the previous examples: - for inserting a new record one can use the code of Ex.10.4 and knowledge on which record spaces are still free - for the delete of a specified record one just replaces that record with a blanc record - for the update of a record one retrieves the record with the code of Ex.10.5 and then the user can modify the information now present in textfields. When finished the record is sent back to the file with code of Ex.10.4. Note that all this work is extremely long and complicated with sequential files, where work with one record will affect the position of all subsequent records.