Creating a Sequential-Access Text File

Một phần của tài liệu Visual C 2012 How to Program _ www.bit.ly/taiho123 (Trang 723 - 732)

2. When the app executes, another compiler (known as the just-in-time compiler

17.5 Creating a Sequential-Access Text File

C# imposesnostructure on files. Thus, the concept of a “record” doesnotexist in C# files.

This means that you must structure files to meet the requirements of your apps. The next few examples use text and special characters to organize our own concept of a “record.”

ClassBankUIForm

The following examples demonstrate file processing in a bank-account maintenance app.

These programs have similar user interfaces, so we created reusable class BankUIForm (Fig. 17.7) to encapsulate a base-class GUI (see the screen capture in Fig. 17.7). Class

BankUIForm(part of theBankLibraryproject with this chapter’s examples) contains four

Labels and fourTextBoxes. MethodsClearTextBoxes(lines 28–40),SetTextBoxValues

(lines 43–64) andGetTextBoxValues(lines 67–78) clear, set the values of and get the val- ues of the text in theTextBoxes, respectively.

1 // Fig. 17.7: BankUIForm.cs

2 // A reusable Windows Form for the examples in this chapter.

3 using System;

4 using System.Windows.Forms;

5

6 namespace BankLibrary 7 {

8 public partial class BankUIForm : Form

9 {

10 protected int TextBoxCount = 4; // number of TextBoxes on Form 11

12 // enumeration constants specify TextBox indices 13 public enum TextBoxIndices

14 {

15 ACCOUNT,

16 FIRST,

17 LAST,

18 BALANCE

19 } // end enum 20

Fig. 17.7 | Base class for GUIs in our file-processing apps. (Part 1 of 3.)

17.5 Creating a Sequential-Access Text File 683

21 // parameterless constructor 22 public BankUIForm()

23 {

24 InitializeComponent();

25 } // end constructor 26

27 // clear all TextBoxes 28 public void ClearTextBoxes()

29 {

30 // iterate through every Control on form 31 foreach ( Control guiControl in Controls )

32 {

33 // determine whether Control is TextBox 34 if ( guiControl is TextBox )

35 {

36 // clear TextBox

37 ( ( TextBox ) guiControl ).Clear();

38 } // end if

39 } // end for

40 } // end method ClearTextBoxes 41

42 // set text box values to string-array values 43 public void SetTextBoxValues( string[] values )

44 {

45 // determine whether string array has correct length 46 if ( values.Length != TextBoxCount )

47 {

48 // throw exception if not correct length

49 throw ( new ArgumentException( "There must be " + 50 ( TextBoxCount ) + " strings in the array" ) );

51 } // end if

52 // set array values if array has correct length

53 else

54 {

55 // set array values to TextBox values

56 accountTextBox.Text =

57 values[ ( int ) TextBoxIndices.ACCOUNT ];

58 firstNameTextBox.Text =

59 values[ ( int ) TextBoxIndices.FIRST ];

60 lastNameTextBox.Text = values[ ( int ) TextBoxIndices.LAST ];

61 balanceTextBox.Text =

62 values[ ( int ) TextBoxIndices.BALANCE ];

63 } // end else

64 } // end method SetTextBoxValues 65

66 // return TextBox values as string array 67 public string[] GetTextBoxValues()

68 {

69 string[] values = new string[ TextBoxCount ];

70

71 // copy TextBox fields to string array

72 values[ ( int ) TextBoxIndices.ACCOUNT ] = accountTextBox.Text;

73 values[ ( int ) TextBoxIndices.FIRST ] = firstNameTextBox.Text;

Fig. 17.7 | Base class for GUIs in our file-processing apps. (Part 2 of 3.)

Usingvisual inheritance(Section 15.13), you can extend this class to create the GUIs for several examples in this chapter. Recall that to reuse classBankUIForm, you must compile the GUI into a class library, then add a reference to the new class library in each project that will reuse it. This library (BankLibrary) is provided with the code for this chapter. You might need to re-add the references to this library in our examples when you copy them to your system, since the library most likely will reside in a different location on your system.

ClassRecord

Figure 17.8 contains classRecordthat Figs. 17.9, 17.11 and 17.12 use for maintaining the information in each record that’s written to or read from a file. This class also belongs to theBankLibraryDLL, so it’s located in the same project as classBankUIForm.

74 values[ ( int ) TextBoxIndices.LAST ] = lastNameTextBox.Text;

75 values[ ( int ) TextBoxIndices.BALANCE ] = balanceTextBox.Text;

76

77 return values;

78 } // end method GetTextBoxValues 79 } // end class BankUIForm

80 } // end namespace BankLibrary

1 // Fig. 17.8: Record.cs

2 // Class that represents a data record.

3

4 namespace BankLibrary 5 {

6 public class Record

7 {

8 9 10 11 12 13 14 15

Fig. 17.8 | Record for sequential-access file-processing apps. (Part 1 of 2.) Fig. 17.7 | Base class for GUIs in our file-processing apps. (Part 3 of 3.)

// auto-implemented Account property public int Account { get; set; } // auto-implemented FirstName property public string FirstName { get; set; } // auto-implemented LastName property public string LastName { get; set; }

17.5 Creating a Sequential-Access Text File 685

Class Record contains auto-implemented properties for instance variables Account,

FirstName,LastNameandBalance(lines 9–18), which collectively represent all the infor- mation for a record. The parameterless constructor (lines 21–24) sets these members by calling the four-argument constructor with0for the account number,string.Emptyfor the first and last name and0.0Mfor the balance. The four-argument constructor (lines 27–

34) sets these members to the specified parameter values.

Using a Character Stream to Create an Output File

ClassCreateFileForm(Fig. 17.9) uses instances of classRecordto create asequential-access filethat might be used in an accounts-receivable system—i.e., a program that organizes data regarding money owed by a company’s credit clients. For each client, the program obtains an account number and the client’s first name, last name and balance (i.e., the amount of money that the client owes to the company for previously received goods and services). The data obtained for each client constitutes a record for that client. In this app, the account number is used as therecord key—files are created and maintained in account- number order. This program assumes that the user enters records in account-number or- der. However, a comprehensive accounts-receivable system would provide asortingcapa- bility, so the user could enter the records in any order.

16 17 18 19

20 // parameterless constructor sets members to default values 21 public Record()

22 : this( 0, string.Empty, string.Empty, 0M )

23 {

24 } // end constructor 25

26 // overloaded constructor sets members to parameter values 27 public Record( int accountValue, string firstNameValue, 28 string lastNameValue, decimal balanceValue )

29 {

30 Account = accountValue;

31 FirstName = firstNameValue;

32 LastName = lastNameValue;

33 Balance = balanceValue;

34 } // end constructor 35 } // end class Record 36 } // end namespace BankLibrary

1 // Fig. 17.9: CreateFileForm.cs 2 // Creating a sequential-access file.

3 using System;

4 using System.Windows.Forms;

5 using System.IO;

Fig. 17.9 | Creating and writing to a sequential-access file. (Part 1 of 5.) Fig. 17.8 | Record for sequential-access file-processing apps. (Part 2 of 2.)

// auto-implemented Balance property public decimal Balance { get; set; }

6 using BankLibrary;

7

8 namespace CreateFile 9 {

10

11 {

12 13

14 // parameterless constructor 15 public CreateFileForm()

16 {

17 InitializeComponent();

18 } // end constructor 19

20 // event handler for Save Button

21 private void saveButton_Click( object sender, EventArgs e )

22 {

23 // create and show dialog box enabling user to save file 24 DialogResult result; // result of SaveFileDialog

25 string fileName; // name of file containing data 26

27 28 29 30 31 32 33

34 // ensure that user clicked "OK"

35 if ( result == DialogResult.OK )

36 {

37 // show error if user specified invalid file 38 if ( fileName == string.Empty )

39 MessageBox.Show( "Invalid File Name", "Error",

40 MessageBoxButtons.OK, MessageBoxIcon.Error );

41 else

42 {

43 // save file via FileStream if user specified valid file

44 try

45 {

46 // open file with write access

47 48 49

50 // sets file to where data is written 51

52

53 // disable Save button and enable Enter button

54 saveButton.Enabled = false;

55 enterButton.Enabled = true;

56 } // end try

57 // handle exception if there's a problem opening the file

Fig. 17.9 | Creating and writing to a sequential-access file. (Part 2 of 5.)

public partial class CreateFileForm : BankUIForm

private StreamWriter fileWriter; // writes data to text file

using ( SaveFileDialog fileChooser = new SaveFileDialog() ) {

fileChooser.CheckFileExists = false; // let user create file result = fileChooser.ShowDialog();

fileName = fileChooser.FileName; // name of file to save data } // end using

FileStream output = new FileStream( fileName, FileMode.OpenOrCreate, FileAccess.Write );

fileWriter = new StreamWriter( output );

17.5 Creating a Sequential-Access Text File 687

58 catch ( IOException )

59 {

60 // notify user if file does not exist

61 MessageBox.Show( "Error opening file", "Error",

62 MessageBoxButtons.OK, MessageBoxIcon.Error );

63 } // end catch

64 } // end else

65 } // end if

66 } // end method saveButton_Click 67

68 // handler for enterButton Click

69 private void enterButton_Click( object sender, EventArgs e )

70 {

71 // store TextBox values string array 72 string[] values = GetTextBoxValues();

73

74 // Record containing TextBox values to output 75 Record record = new Record();

76

77 // determine whether TextBox account field is empty

78 if ( values[ ( int ) TextBoxIndices.ACCOUNT ] != string.Empty )

79 {

80 // store TextBox values in Record and output it

81 try

82 {

83 // get account-number value from TextBox 84 int accountNumber = Int32.Parse(

85 values[ ( int ) TextBoxIndices.ACCOUNT ] );

86

87 // determine whether accountNumber is valid 88 if ( accountNumber > 0 )

89 {

90 // store TextBox fields in Record

91 record.Account = accountNumber;

92 record.FirstName = values[ ( int )

93 TextBoxIndices.FIRST ];

94 record.LastName = values[ ( int )

95 TextBoxIndices.LAST ];

96 record.Balance = Decimal.Parse(

97 values[ ( int ) TextBoxIndices.BALANCE ] );

98

99 // write Record to file, fields separated by commas 100

101 102

103 } // end if

104 else

105 {

106 // notify user if invalid account number

107 MessageBox.Show( "Invalid Account Number", "Error",

108 MessageBoxButtons.OK, MessageBoxIcon.Error );

109 } // end else

110 } // end try

Fig. 17.9 | Creating and writing to a sequential-access file. (Part 3 of 5.)

fileWriter.WriteLine(

record.Account + "," + record.FirstName + "," + record.LastName + "," + record.Balance );

111 // notify user if error occurs during the output operation 112 catch ( IOException )

113 {

114 MessageBox.Show( "Error Writing to File", "Error",

115 MessageBoxButtons.OK, MessageBoxIcon.Error );

116 } // end catch

117 // notify user if error occurs regarding parameter format 118 catch ( FormatException )

119 {

120 MessageBox.Show( "Invalid Format", "Error",

121 MessageBoxButtons.OK, MessageBoxIcon.Error );

122 } // end catch

123 } // end if

124

125 ClearTextBoxes(); // clear TextBox values 126 } // end method enterButton_Click

127

128 // handler for exitButton Click

129 private void exitButton_Click( object sender, EventArgs e )

130 {

131 // determine whether file exists 132 if ( fileWriter != null )

133 {

134 try

135 {

136 // close StreamWriter and underlying file 137

138 } // end try

139 // notify user of error closing file 140 catch ( IOException )

141 {

142 MessageBox.Show( "Cannot close file", "Error",

143 MessageBoxButtons.OK, MessageBoxIcon.Error );

144 } // end catch

145 } // end if

146

147 Application.Exit();

148 } // end method exitButton_Click 149 } // end class CreateFileForm 150 } // end namespace CreateFile

Fig. 17.9 | Creating and writing to a sequential-access file. (Part 4 of 5.)

fileWriter.Close();

a) BankUI graphical user interface with three additional controls

17.5 Creating a Sequential-Access Text File 689

Class CreateFileForm either creates or opens a file (depending on whether one exists), then allows the user to write records to it. Theusingdirective in line 6 enables us to use the classes of theBankLibrarynamespace; this namespace contains classBankUI-

Form, from which classCreateFileForminherits (line 10). ClassCreateFileForm’s GUI enhances that of classBankUIFormwith buttonsSave As,EnterandExit.

MethodsaveButton_Click

When the user clicks the Save As button, the program invokes the event handler

saveButton_Click(lines 21–66). Line 27 instantiates an object of classSaveFileDialog

(namespaceSystem.Windows.Forms). By placing this object in ausingstatement (lines 27–32), we can ensure that the dialog’sDisposemethod is called to release its resources as soon as the program has retrieved user input from it.SaveFileDialogobjects are used for selecting files (see the second screen in Fig. 17.9). Line 29 indicates that the dialog should not check if the file name specified by the user already exists (this is actually the default).

Fig. 17.9 | Creating and writing to a sequential-access file. (Part 5 of 5.)

Files and directories b) Save File dialog

c) Account 100,

"Nancy Brown", saved with a balance of -25.54

Line 30 callsSaveFileDialogmethodShowDialogto display the dialog. When displayed, aSaveFileDialogprevents the user from interacting with any other window in the pro- gram until the user closes theSaveFileDialogby clicking eitherSaveorCancel. Dialogs that behave in this manner are calledmodal dialogs. The user selects the appropriate drive, directory and file name, then clicksSave. MethodShowDialogreturns a DialogResult

specifying which button (SaveorCancel) the user clicked to close the dialog. This is as- signed toDialogResultvariableresult(line 30). Line 31 gets the file name from the di- alog. Line 35 tests whether the user clicked OK by comparing this value to

DialogResult.OK. If the values are equal, methodsaveButton_Clickcontinues.

You can open files to perform text manipulation by creating objects of classFileStream. In this example, we want the file to be opened foroutput, so lines 47–48 create aFileStream object. TheFileStreamconstructor that we use receives three arguments—astringcon- taining the path and name of the file to open, a constant describing how to open the file and a constant describing the file permissions. The constantFileMode.OpenOrCreate(line 48) indicates that theFileStreamobject should open the file if itexistsor create the file if itdoes not exist. Note that the contents of an existing file areoverwrittenby theStreamWriter. To preservethe original contents of a file, useFileMode.Append. There are otherFileModecon- stants describing how to open files; we introduce these constants as we use them in examples.

The constantFileAccess.Writeindicates that the program can perform onlywriteopera- tions with theFileStreamobject. There are two other constants for the third constructor parameter—FileAccess.Readfor read-only access and FileAccess.ReadWrite for both read and write access. Line 58 catches anIOExceptionif there’s a problem opening the file or creating theStreamWriter. If so, the program displays an error message (lines 61–62). If no exception occurs, the file is open for writing.

MethodenterButton_Click

After typing information into each TextBox, the user clicks Enter, which calls enter-

Button_Click(lines 69–126) to save the data from theTextBoxes into the user-specified file. If the user entered avalidaccount number (i.e., an integer greater than zero), lines 91–

97 store theTextBox values in an object of typeRecord(created at line 75). If the user enteredinvaliddata in one of theTextBoxes (such as nonnumeric characters in theBalance

field), the program throws aFormatException. Thecatchblock in lines 118–122 handles such exceptions by notifying the user (via aMessageBox) of the improper format.

If the user enteredvaliddata, lines 100–102 write the record to the file by invoking method WriteLine of the StreamWriter object that was created at line 51. Method

WriteLine writes a sequence of characters to a file. The StreamWriter object is con- structed with aFileStreamargument that specifies the file to which theStreamWriter

will output text. ClassStreamWriter(like most of the classes we discuss in this chapter) belongs to t he System.IOnamespace.

Good Programming Practice 17.1

When opening files, use theFileAccessenumerationto control user access to these files.

Common Programming Error 17.1

Failure to open a file before attempting to use it in a program is a logic error.

Một phần của tài liệu Visual C 2012 How to Program _ www.bit.ly/taiho123 (Trang 723 - 732)

Tải bản đầy đủ (PDF)

(1.020 trang)