Sequential-Access Text Files
In this section, we create and manipulate sequential-access files. As mentioned earlier, these are files in which records are stored in order by the record-key field. We first demonstrate sequential-access files using text files, allowing the reader to quickly create and edit human-readable files. In the subsections of this chapter we discuss creating, writing data to, reading data from and updating sequential-access text files. We also include a credit-inquiry program that retrieves specific data from a file.
14.5.1. Creating a Sequential-Access Text File
Java imposes no structure on a filenotions such as a record do not exist as part of the Java language. Therefore, the programmer must structure files to meet the requirements of the intended application. In the following example, we see how to impose a record structure on a file.
The program in Fig. 14.6Fig. 14.7 and Fig. 14.9 creates a simple sequential-access file that might be used in an accounts receivable system to help keep track of the amounts owed to a company by its credit clients. For each client, the program obtains from the user an account number, the client's name and the client's balance (i.e., the amount the client owes the company for goods and services received). The data obtained for each client constitutes a "record" for that client. The account number is used as the record key in this applicationthe file will be created and maintained in account number order. The program assumes that the user enters the records in account number order. In a comprehensive accounts receivable system (based on sequential-access files), a sorting capability would be provided so that the user could enter the records in any order. The records would then be sorted and written to the file.
Figure 14.6. AccountRecord maintains information for one account.
(This item is displayed on pages 683 - 684 in the print version)
1 // Fig. 14.6: AccountRecord.java 2 // A class that represents one record of information. 3 package com.deitel.jhtp6.ch14; // packaged for reuse 4 5 public class AccountRecord 6 { 7 private int account; 8 private String firstName; 9 private String lastName; 10 private double balance; 11 12 // no-argument constructor calls other constructor with default values 13 public AccountRecord() 14 { 15 this ( 0, "", "", 0.0 ); // call four-argument constructor 16 } // end no-argument AccountRecord constructor 17 18 // initialize a record 19 public AccountRecord( int acct, String first, String last, double bal ) 20 { 21 setAccount( acct ); 22 setFirstName( first ); 23 setLastName( last ); 24 setBalance( bal ); 25 } // end four-argument AccountRecord constructor 26 27 // set account number 28 public void setAccount( int acct ) 29 { 30 account = acct; 31 } // end method setAccount 32 33 // get account number 34 public int getAccount() 35 { 36 return account; 37 } // end method getAccount 38 39 // set first name 40 public void setFirstName( String first ) 41 { 42 firstName = first; 43 } // end method setFirstName 44 45 // get first name 46 public String getFirstName() 47 { 48 return firstName; 49 } // end method getFirstName 50 51 // set last name 52 public void setLastName( String last ) 53 { 54 lastName = last; 55 } // end method setLastName 56 57 // get last name 58 public String getLastName() 59 { 60 return lastName; 61 } // end method getLastName 62 63 // set balance 64 public void setBalance( double bal ) 65 { 66 balance = bal; 67 } // end method setBalance 68 69 // get balance 70 public double getBalance() 71 { 72 return balance; 73 } // end method getBalance 74 } // end class AccountRecord |
Figure 14.7. Creating a sequential text file.
(This item is displayed on pages 686 - 687 in the print version)
1 // Fig. 14.7: CreateTextFile.java 2 // Writing data to a text file with class Formatter. 3 import java.io.FileNotFoundException; 4 import java.lang.SecurityException; 5 import java.util.Formatter; 6 import java.util.FormatterClosedException; 7 import java.util.NoSuchElementException; 8 import java.util.Scanner; 9 10 import com.deitel.jhtp6.ch14.AccountRecord; 11 12 public class CreateTextFile 13 { 14 private Formatter output; // object used to output text to file 15 16 // enable user to open file 17 public void openFile() 18 { 19 try 20 { 21 output = new Formatter( "clients.txt" ); 22 } // end try 23 catch ( SecurityException securityException ) 24 { 25 System.err.println( 26 "You do not have write access to this file." ); 27 System.exit( 1 ); 28 } // end catch 29 catch ( FileNotFoundException filesNotFoundException ) 30 { 31 System.err.println( "Error creating file." ); 32 System.exit( 1 ); 33 } // end catch 34 } // end method openFile 35 36 // add records to file 37 public void addRecords() 38 { 39 // object to be written to file 40 AccountRecord record = new AccountRecord(); 41 42 Scanner input = new Scanner( System.in ); 43 44 System.out.printf( "%s %s %s %s ", 45 "To terminate input, type the end-of-file indicator ", 46 "when you are prompted to enter input.", 47 "On UNIX/Linux/Mac OS X type d then press Enter", 48 "On Windows type z then press Enter" ); 49 50 System.out.printf( "%s %s", 51 "Enter account number (> 0), first name, last name and balance.", 52 "? " ); 53 54 while ( input.hasNext() ) // loop until end-of-file indicator 55 { 56 try // output values to file 57 { 58 // retrieve data to be output 59 record.setAccount( input.nextInt() ); // read account number 60 record.setFirstName( input.next() ); // read first name 61 record.setLastName( input.next() ); // read last name 62 record.setBalance( input.nextDouble() ); // read balance 63 64 if ( record.getAccount() > 0 ) 65 { 66 // write new record 67 output.format( "%d %s %s %.2f ", record.getAccount(), 68 record.getFirstName(), record.getLastName(), 69 record.getBalance() ); 70 } // end if 71 else 72 { 73 System.out.println( 74 "Account number must be greater than 0." ); 75 } // end else 76 } // end try 77 catch ( FormatterClosedException formatterClosedException ) 78 { 79 System.err.println( "Error writing to file." ); 80 return; 81 } // end catch 82 catch ( NoSuchElementException elementException ) 83 { 84 System.err.println( "Invalid input. Please try again." ); 85 input.nextLine(); // discard input so user can try again 86 } // end catch 87 88 System.out.printf( "%s %s %s", "Enter account number (>0),", 89 "first name, last name and balance.", "? " ); 90 } // end while 91 } // end method addRecords 92 93 // close file 94 public void closeFile() 95 { 96 if ( output != null ) 97 output.close(); 98 } // end method closeFile 99 } // end class CreateTextFile |
Class AccountRecord (Fig. 14.6) encapsulates the client record information (i.e., account, first name, etc.) used by the examples in this chapter. The class AccountRecord is declared in package com.deitel.jhtp6.ch14 (line 3), so that it can be imported into several examples. Class AccountRecord contains private data members account, firstName, lastName and balance (lines 710). This class also provides public set and get methods for accessing the private fields.
Compile class AccountRecord as follows:
javac -d c:examplesch14 comdeiteljhtp6ch14AccountRecord.java
This places AccountRecord.class in its package directory structure and places the package in c:examplesch14. When you compile class AccountRecord (or any other classes that will be reused in this chapter), you should place them in a common directory (e.g., c:examplesch14). When you compile or execute classes that use AccountRecord (e.g., CreateTextFile in Fig. 14.7), you must specify the command-line argument - classpath to both javac and java, as in
javac -classpath .;c:examplesch14 CreateTextFile.java java -classpath .;c:examplesch14 CreateTextFile
Note that the current directory (specified with .) is included in the classpath. This ensures that the compiler can locate other classes in the same directory as the class being compiled. The path separator used in the preceding commands should be the one that is appropriate for your platformfor example, a semicolon (;) on Windows and a colon (:) on UNIX/ Linux/Mac OS X.
Now let us examine class CreateTextFile (Fig. 14.7). Line 14 declares Formatter variable output. As discussed in Section 14.3, a Formatter object outputs formatted strings, using the same formatting capabilities as method System.out.printf. A Formatter object can output to various locations, such as the screen or a file, as is done here. The Formatter object is instantiated in line 21 in method openFile (lines 1734). The constructor used in line 21 takes one argumenta String containing the name of the file, including its path. If a path is not specified, as is the case here, the JVM assumes that the files is in the directory from which the program was executed. For text files, we use the .txt file extension. If the file does not exist, it will be created. If an existing file is opened, the contents of the file are truncatedall the data in the file is discarded. At this point the file is open for writing, and the resulting Formatter object can be used to write data to the file. Lines 2328 handle the SecurityException, which occurs if the user does not have permission to write data to the file. Lines 2933 handle the FileNotFoundException, which occurs if the file does not exist and a new file cannot be created. This exception may also occur if there is an error opening the file. Note that in both exception handlers, we call static method System.exit, and pass the value 1. This method terminates the application. An argument of 0 to method exit indicates successful program termination. A nonzero value, such as 1 in this example, normally indicates that an error has occurred. This value is passed to the command window that executed the program. The argument is useful if the program is executed from a batch file on Windows systems or a shell script on UNIX/Linux/Mac OS X systems. Batch files and shell scripts offer a convenient way of executing several programs in sequence. When the first program ends, the next program begins execution. It is possible to use the argument to method exit in a batch file or shell script to determine whether other programs should execute. For more information on batch files or shell scripts, see your operating system's documentation.
Method addRecords (lines 3791) prompts the user to enter the various fields for each record or to enter the end-of-file key sequence when data entry is complete. Figure 14.8 lists the key combinations for entering end-of-file for various computer systems.
Operating system |
Key combination |
---|---|
UNIX/Linux/Mac OS X |
d |
Windows |
z |
Line 40 creates an AccountRecord object, which will be used to store the values of the current record entered by the user. Line 42 creates a Scanner object to read input from the user at the keyboard. Lines 4448 and 5052 prompt the user for input.
Line 54 uses Scanner method hasNext to determine whether the end-of-file key combination has been entered. The loop executes until hasNext encounters the end-of-file indicators.
Lines 5962 read data from the user, storing the record information in the AccountRecord object. Each statement throws a NoSuchElementException (handled in lines 8286) if the data is in the wrong format (e.g., a string when an int is expected) or if there is no more data to input. If the account number is greater than 0 (line 64), the record's information is written to clients.txt (lines 6769) using method format. This method can perform identical formatting to the System.out.printf method used extensively in earlier chapters. This method outputs a formatted string to the output destination of the Formatter object, in this case the file clients.txt. The format string "%d %s %s %.2 " indicates that the current record will be stored as an integer (the account number) followed by a string (the first name), another string (the last name) and a floating-point value (the balance). Each piece of information is separated from the next by a space and the double value (the balance) is output with two digits to the right of the decimal point. The data in the text file can be viewed with a text editor, or retrieved later by a program designed to read the file (Section 14.5.2). When lines 6769 execute, if the Formatter object is closed, a FormatterClosedException will be thrown (handled in lines 7781). [Note: You can also output data to a text file using class java.io.PrintWriter, which also provides method format for outputting formatted data.]
Lines 9498 declare method closeFile, which closes the Formatter and the underlying output file. Line 97 closes the object by simply calling method close. If method close is not called explicitly, the operating system normally will close the file when program execution terminates. This is an example of operating system "housekeeping."
Figure 14.9 runs the program. Line 8 creates a CreateTextFile object, which is then used to open, add records to and close the file (lines 1012). The sample data for this application is shown in Fig. 14.10. In the sample execution for this program, the user enters information for five accounts, then enters end-of-file to signal that data entry is complete. The sample execution does not show how the data records actually appear in the file. In the next section to verify that the file has been created successfully, we present a program that reads the file and prints its contents. Because this is a text file, you can also verify the information by opening the file in a text editor.
Figure 14.9. Testing the CreateTextFile class.
(This item is displayed on page 689 in the print version)
1 // Fig. 14.9: CreateTextFileTest.java 2 // Testing the CreateTextFile class. 3 4 public class CreateTextFileTest 5 { 6 public static void main( String args[] ) 7 { 8 CreateTextFile application = new CreateTextFile(); 9 10 application.openFile(); 11 application.addRecords(); 12 application.closeFile(); 13 } // end main 14 } // end class CreateTextFileTest
|
Sample data |
|||
---|---|---|---|
100 |
Bob |
Jones |
24.98 |
200 |
Steve |
Doe |
-345.67 |
300 |
Pam |
White |
0.00 |
400 |
Sam |
Stone |
-42.16 |
500 |
Sue |
Rich |
224.62 |
14.5.2. Reading Data from a Sequential-Access Text File
Data is stored in files so that it may be retrieved for processing when needed. Section 14.5.1 demonstrated how to create a file for sequential access. This section shows how to read data sequentially from a text file. In this section, we demonstrate how class Scanner can be used to input data from a file rather than the keyboard.
The application in Fig. 14.11 and Fig. 14.12 reads records from the file "clients. txt" created by the application of Section 14.5.1 and displays the record contents. Line 13 of Fig. 14.11 declares a Scanner that will be used to retrieve input from the file.
Figure 14.11. Sequential file reading using a Scanner.
(This item is displayed on pages 690 - 691 in the print version)
1 // Fig. 14.11: ReadTextFile.java 2 // This program reads a text file and displays each record. 3 import java.io.File; 4 import java.io.FileNotFoundException; 5 import java.lang.IllegalStateException; 6 import java.util.NoSuchElementException; 7 import java.util.Scanner; 8 9 import com.deitel.jhtp6.ch14.AccountRecord; 10 11 public class ReadTextFile 12 { 13 private Scanner input; 14 15 // enable user to open file 16 public void openFile() 17 { 18 try 19 { 20 input = new Scanner( new File( "clients.txt" ) ); 21 } // end try 22 catch ( FileNotFoundException fileNotFoundException ) 23 { 24 System.err.println( "Error opening file." ); 25 System.exit( 1 ); 26 } // end catch 27 } // end method openFile 28 29 // read record from file 30 public void readRecords() 31 { 32 // object to be written to screen 33 AccountRecord record = new AccountRecord(); 34 35 System.out.printf( "%-10s%-12s%-12s%10s ", "Account", 36 "First Name", "Last Name", "Balance" ); 37 38 try // read records from file using Scanner object 39 { 40 while ( input.hasNext() ) 41 { 42 record.setAccount( input.nextInt() ); // read account number 43 record.setFirstName( input.next() ); // read first name 44 record.setLastName( input.next() ); // read last name 45 record.setBalance( input.nextDouble() ); // read balance 46 47 // display record contents 48 System.out.printf( "%-10d%-12s%-12s%10.2f ", 49 record.getAccount(), record.getFirstName(), 50 record.getLastName(), record.getBalance() ); 51 } // end while 52 } // end try 53 catch ( NoSuchElementException elementException ) 54 { 55 System.err.println( "File improperly formed." ); 56 input.close(); 57 System.exit( 1 ); 58 } // end catch 59 catch ( IllegalStateException stateException ) 60 { 61 System.err.println( "Error reading from file." ); 62 System.exit( 1 ); 63 } // end catch 64 } // end method readRecords 65 66 // close file and terminate application 67 public void closeFile() 68 { 69 if ( input != null ) 70 input.close(); // close file 71 } // end method closeFile 72 } // end class ReadTextFile |
Figure 14.12. Testing the ReadTextFile class.
(This item is displayed on page 691 in the print version)
1 // Fig. 14.12: ReadTextFileTest.java 2 // This program test class ReadTextFile. 3 4 public class ReadTextFileTest 5 { 6 public static void main( String args[] ) 7 { 8 ReadTextFile application = new ReadTextFile(); 9 10 application.openFile(); 11 application.readRecords(); 12 application.closeFile(); 13 } // end main 14 } // end class ReadTextFileTest
|
Method openFile (lines 1627) opens the file for reading by instantiating a Scanner object in line 20. We pass a File object to the constructor, which specifies that the Scanner object will read from the file "clients.txt" located in the directory from which the application executes. If the file cannot be found, a FileNotFoundException occurs. The exception is handled in lines 2226.
Method readRecords (lines 3064) reads and displays records from the file. Line 33 creates AccountRecord object record to store the current record's information. Lines 3536 display headers for the columns in the application's output. Lines 4051 read data from the file until the end-of-file marker is reached (in which case, method hasNext will return false at line 40). Lines 4245 use Scanner methods nextInt, next and nexTDouble to input an integer (the account number), two strings (the first and last names) and a double value (the balance). Each record is one line of data in the file. The values are stored in object record. If the information in the file is not properly formed (e.g., there is a last name where there should be a balance), a NoSuchElementException occurs when the record is input. This exception is handled in lines 5358. If the Scanner was closed before the data was input, an IllegalStateException occurs (handled in lines 5963). If no exceptions occur, the record's information is displayed on the screen (lines 4850). Note in the format string in line 48 that the account number, first name and last name are left justified, while the balance is right justified and output with two digits of precision. Each iteration of the loop inputs one line of text from the text file, which represents one record.
Lines 6771 define method closeFile, which closes the Scanner. Method main is defined in Fig. 14.12, in lines 613. Line 8 creates a ReadTextFile object, which is then used to open, add records to and close the file (lines 1012).
14.5.3. Case Study: A Credit-Inquiry Program
To retrieve data sequentially from a file, programs normally start reading from the beginning of the file and read all the data consecutively until the desired information is found. It might be necessary to process the file sequentially several times (from the beginning of the file) during the execution of a program. Class Scanner does not provide the ability to reposition to the beginning of the file. If it is necessary to read the file again, the program must close the file and reopen it.
The program in Fig. 14.13Fig. 14.15 allows a credit manager to obtain lists of customers with zero balances (i.e., customers who do not owe any money), customers with credit balances (i.e., customers to whom the company owes money) and customers with debit balances (i.e., customers who owe the company money for goods and services received). A credit balance is a negative amount, and a debit balance is a positive amount.
Figure 14.13. Enumeration for menu options.
1 // Fig. 14.13: MenuOption.java 2 // Defines an enum type for the credit inquiry program's options. 3 4 public enum MenuOption 5 { 6 // declare contents of enum type 7 ZERO_BALANCE( 1 ), 8 CREDIT_BALANCE( 2 ), 9 DEBIT_BALANCE( 3 ), 10 END( 4 ); 11 12 private final int value; // current menu option 13 14 MenuOption( int valueOption ) 15 { 16 value = valueOption; 17 } // end MenuOptions enum constructor 18 19 public int getValue() 20 { 21 return value; 22 } // end method getValue 23 } // end enum MenuOption |
Figure 14.14. Credit-inquiry program.
(This item is displayed on pages 693 - 695 in the print version)
1 // Fig. 14.14: CreditInquiry.java 2 // This program reads a file sequentially and displays the 3 // contents based on the type of account the user requests 4 // (credit balance, debit balance or zero balance). 5 import java.io.File; 6 import java.io.FileNotFoundException; 7 import java.lang.IllegalStateException; 8 import java.util.NoSuchElementException; 9 import java.util.Scanner; 10 11 import com.deitel.jhtp6.ch14.AccountRecord; 12 13 public class CreditInquiry 14 { 15 private MenuOption accountType; 16 private Scanner input; 17 private MenuOption choices[] = { MenuOption.ZERO_BALANCE, 18 MenuOption.CREDIT_BALANCE, MenuOption.DEBIT_BALANCE, 19 MenuOption.END }; 20 21 // read records from file and display only records of appropriate type 22 private void readRecords() 23 { 24 // object to be written to file 25 AccountRecord record = new AccountRecord(); 26 27 try // read records 28 { 29 // open file to read from beginning 30 input = new Scanner( new File( "clients.txt" ) ); 31 32 while ( input.hasNext() ) // input the values from the file 33 { 34 record.setAccount( input.nextInt() ); // read account number 35 record.setFirstName( input.next() ); // read first name 36 record.setLastName( input.next() ); // read last name 37 record.setBalance( input.nextDouble() ); // read balance 38 39 // if proper acount type, display record 40 if ( shouldDisplay( record.getBalance() ) ) 41 System.out.printf( "%-10d%-12s%-12s%10.2f ", 42 record.getAccount(), record.getFirstName(), 43 record.getLastName(), record.getBalance() ); 44 } // end while 45 } // end try 46 catch ( NoSuchElementException elementException ) 47 { 48 System.err.println( "File improperly formed." ); 49 input.close(); 50 System.exit( 1 ); 51 } // end catch 52 catch ( IllegalStateException stateException ) 53 { 54 System.err.println( "Error reading from file." ); 55 System.exit( 1 ); 56 } // end catch 57 catch ( FileNotFoundException fileNotFoundException ) 58 { 59 System.err.println( "File cannot be found." ); 60 System.exit( 1 ); 61 } // end catch 62 finally 63 { 64 if ( input != null ) 65 input.close(); // close the Scanner and the file 66 } // end finally 67 } // end method readRecords 68 69 // use record type to determine if record should be displayed 70 private boolean shouldDisplay( double balance ) 71 { 72 if ( ( accountType == MenuOption.CREDIT_BALANCE ) 73 && ( balance < 0 ) ) 74 return true; 75 76 else if ( ( accountType == MenuOption.DEBIT_BALANCE ) 77 && ( balance > 0 ) ) 78 return true; 79 80 else if ( ( accountType == MenuOption.ZERO_BALANCE ) 81 && ( balance == 0 ) ) 82 return true; 83 84 return false; 85 } // end method shouldDisplay 86 87 // obtain request from user 88 private MenuOption getRequest() 89 { 90 Scanner textIn = new Scanner( System.in ); 91 int request = 1 ; 92 93 // display request options 94 System.out.printf( " %s %s %s %s %s ", 95 "Enter request", " 1 - List accounts with zero balances", 96 " 2 - List accounts with credit balances", 97 " 3 - List accounts with debit balances", " 4 - End of run" ); 98 99 try // attempt to input menu choice 100 { 101 do // input user request 102 { 103 System.out.print( " ? " ); 104 request = textIn.nextInt(); 105 } while ( ( request < 1 ) || ( request > 4 ) ); 106 } // end try 107 catch ( NoSuchElementException elementException ) 108 { 109 System.err.println( "Invalid input." ); 110 System.exit( 1 ); 111 } // end catch 112 113 return choices[ request - 1 ]; // return enum value for option 114 } // end method getRequest 115 116 public void processRequests() 117 { 118 // get user's request (e.g., zero, credit or debit balance) 119 accountType = getRequest(); 120 121 while ( accountType != MenuOption.END ) 122 { 123 switch ( accountType ) 124 { 125 case ZERO_BALANCE: 126 System.out.println( " Accounts with zero balances: " ); 127 break; 128 case CREDIT_BALANCE: 129 System.out.println( " Accounts with credit balances: " ); 130 break ; 131 case DEBIT_BALANCE: 132 System.out.println( " Accounts with debit balances: " ); 133 break; 134 } // end switch 135 136 readRecords(); 137 accountType = getRequest(); 138 } // end while 139 } // end method processRequests 140 } // end class CreditInquiry |
Figure 14.15. Testing the CreditInquiry class.
(This item is displayed on page 696 in the print version)
1 // Fig. 14.15: CreditInquiryTest.java 2 // This program tests class CreditInquiry. 3 4 public class CreditInquiryTest 5 { 6 public static void main( String args[] ) 7 { 8 CreditInquiry application = new CreditInquiry(); 9 application.processRequests(); 10 } // end main 11 } // end class CreditInquiryTest |
We begin by creating an enum type (Fig. 14.13) to define the different menu options the user will have. The options and their values are listed in lines 710. Method getValue (lines 1922) retrieves the value of a specific enum constant.
Figure 14.14 contains the functionality for the credit-inquiry program, and Fig. 14.15 contains the main method that executes the program. The program displays a text menu and allows the credit manager to enter one of three options to obtain credit information. Option 1 (ZERO_BALANCE) produces a list of accounts with zero balances. Option 2 (CREDIT_BALANCE) produces a list of accounts with credit balances. Option 3 (DEBIT_BALANCE) produces a list of accounts with debit balances. Option 4 (END) terminates program execution. A sample output is shown in Fig. 14.16.
Figure 14.16. Sample output of the credit-inquiry program in Fig. 14.15.
(This item is displayed on page 696 in the print version)
|
The record information is collected by reading through the entire file and determining whether each record satisfies the criteria for the account type selected by the credit manager. Method processRequests (lines 116139 of Fig. 14.14) calls method getrequest to display the menu options (line 119) and stores the result in MenuOption variable accountType. Note that getrequest TRanslates the number typed by the user into a MenuOption by using the number to select a MenuOption from array choices. Lines 121138 loop until the user specifies that the program should terminate. The switch statement in lines 123134 displays a header for the current set of records to be output to the screen. Line 136 calls method readRecords (lines 2267), which loops through the file and reads every record.
Line 30 of method readRecords opens the file for reading with a Scanner. Note that the file will be opened for reading with a new Scanner object each time this method is called, so that we can again read from the beginning of the file. Lines 3437 read a record. Line 40 calls method shouldDisplay (lines 7085) to determine whether the current record satisfies the account type requested. If shouldDisplay returns TRue, the program displays the account information. When the end-of-file marker is reached, the loop terminates and line 65 calls the Scanner's close method to close the Scanner and the file. Notice that this occurs in a finally block, which will execute whether or not the file was successfully read. Once all the records have been read, control returns to method processRequests and geTRequest is again called (line 137) to retrieve the user's next menu option. Figure 14.15 contains method main, and calls method processRequests in line 9.
14.5.4. Updating Sequential-Access Files
The data in many sequential files cannot be modified without the risk of destroying other data in the file. For example, if the name "White" needed to be changed to "Worthington," the old name cannot simply be overwritten because the new name requires more space. The record for White was written to the file as
300 Pam White 0.00
If the record is rewritten beginning at the same location in the file using the new name, the record would be
300 Pam Worthington 0.00
The new record is larger (has more characters) than the original record. The characters beyond the second "o" in "Worthington" would overwrite the beginning of the next sequential record in the file. The problem here is that fields in a text fileand hence recordscan vary in size. For example, 7, 14,117, 2074 and 27383 are all ints stored in the same number of bytes (4) internally, but they are different-sized fields when displayed on the screen or written to a file as text.
Therefore, records in a sequential-access file are not usually updated in place. Instead, the entire file is usually rewritten. To make the preceding name change, the records before 300 Pam White 0.00 in a sequential-access file would be copied to a new file, the new record (which can be of a different size than the one it replaces) would be written and the records after 300 Pam White 0.00 would be copied to the new file. It is uneconomical just to update one record, but reasonable if a substantial portion of the records needs to be updated.