Marco Web Center |
One of the peculiarities of Pascal compared with other programming languages is its built-in support for files. As you might recall from Chapter 2, the language has a file keyword, which is a type specifier, like array or record. You use file to define a new type, and then you can use the new data type to declare new variables: type IntFile: file of Integers; var IntFile1: IntFile; It is also possible to use the file keyword without indicating a data type, to specify an untyped file. Alternatively, you can use the TextFile type, defined in the System units to declare files of ASCII characters. Each kind of file has its own predefined routines, as we will see later in this chapter. Routines for Working with FilesOnce you have declared a file variable, you can assign it to a real file in the file system using the AssignFile method. The next step is usually to call Reset to open the file for reading at the beginning, Rewrite to open (or create) it for writing, and Append to add new items to the end of the file without removing the older items. Once the input or output operations are done, you should call CloseFile. As an example look at the folloing code, which simply saves some numbers to a file: type IntFile: file of Integers; var IntFile1: IntFile; begin AssignFile (IntFile1, 'c:/tmp/test.my') Rewrite (IntFile1); Write (IntFile1, 1); Write (IntFile1, 2); CloseFile (IntFile1); end; The CloseFile operation should typically be done inside a finally block, to avoid leaving the file open in case the file handling code generates an exception. Actually file based operations generate exceptiosn or not depending on the $I compiler settings. In case the system doesn't raise exceptions, you can check the IOResult global variable to see if anything went wrong. Delphi includes many other file management routines, some of whcih are includes in the list below:
Not all of these routines are defined in standard Pascal, but many of them have been part of Borland Pascal for a long time. You can find detailed information about these routines in Delphi's Help files. Here, I'll show you three simple examples to demonstrate how these features can be used. Handling Text FilesOne of the most commonly used file formats is that of text files. As I mentioned before, Delphi has some specific support for text files, most notably the TextFile data type defined by the System unit. Ignoring the fact that Delphi's string lists can autoamtically save themselves to a file (with the SaveToFile method, based on the use of streams), you could easily save the content of a string list to a TextFile by writing the folloing code (in which the file name is requested to the user, using Delph's SaveDialog component): var OutputFile: TextFile; I: Integer; begin // choose a file if SaveDialog1.Execute then begin // output the text to a file AssignFile (OutputFile, SaveDialog1.FileName); Rewrite (OutputFile); try // save listbox data to file for I := 0 to ListBox1.Items.Count - 1 do Writeln (OutputFile, ListBox1.Items[I]); finally CloseFile (OutputFile); end; end; end; Instead of being connected to a physical file, a Pascal file type variable can be hooked directly to the printer, so that the output will be printed instead of beign saved to a file. To accomplish this, simple use the AssignPrn procedure. For example, in the code above you could replace the line AssignFile (OutputFile, SaveDialog1.FileName); with the line AssignPrn (OutputFile); A Text File ConverterUp to now we've seen simple examples of creating new files. In our next example, we'll process an existing file, creating a new one with a modified version of the contents. The program, named Filter, can convert all the characters in a text file to uppercase, capitalize only the initial word of each sentence, or ignore the characters from the upper portion of the ASCII character set. The form of the program has two read-only edit boxes for the names of the input and output files, and two buttons to select input and output files using the standard dialog boxes. The form's lower portion contains a RadioGroup component and a bitmap button (named ConvertBitBtn) to apply the current conversion to the selected files. The radio group has three items, as you can see from the following portion of the form's textual description: object RadioGroup1: TRadioGroup Caption = 'Conversion' Items.Strings = ( '&Uppercase' 'Capitalize &sentences' 'Remove s&ymbols') The user can click on the two buttons to choose the names of the input and output files, displayed in the two edit boxes: procedure TForm1.Button1Click(Sender: TObject); begin if OpenDialog1.Execute then Edit1.Text := OpenDialog1.Filename; end; The second button activates the SaveDialog1 dialog box. The real code of the example is in the three conversion routines that are called by the bitmap button's OnClick event-handler. These calls take place inside a case statement in the middle of the ConvertBitBtn button's OnClick handler: case RadioGroup1.ItemIndex of 0: ConvUpper; 1: ConvCapitalize; 2: ConvSymbols; end; Once again, you can see the entire source code among the download files. Before calling one of the conversion procedures, the ConvertBitBtnClick method displays a dialog box (ConvertForm) with a ProgressBar component, to show the user that the conversion is taking place (as you can see in Figure 12.1). This method does most of the work related to handling the files-it opens the input file as a file of bytes (a file storing data as plain bytes) the first time, so that it can use the FileSize procedure, which is not available for text files. Then this file is closed and reopened as a text file. FIGURE 12.1: The conversion procedures update the secondary form's progress bar to let the user see the percentage of the file already processed. Since the program opens two files, and each of these operations can fail, it uses two nested try blocks to ensure a high level of protection, although using the standard dialog boxes to select file names already provides a good confirmation of file selection. Now, let's take a look at one of the conversion routines in detail. The simplest of the three conversion routines is ConvUpper, which converts every character in the text file to uppercase. Here is its code: procedure TForm1.ConvUpper; var Ch: Char; Position: LongInt; begin Position := 0; while not Eof (FileIn) do begin Read (FileIn, Ch); Ch := UpCase (Ch); Write (FileOut, Ch); Inc (Position); ConvertForm.ProgressBar1.Position := Position * 100 div FileLength; Application.ProcessMessages; end; end; This method reads each character from the source file until the program reaches the end of the file (Eof). Each single character is converted and copied to the output file. As an alternative, it is possible to read and convert one line at a time (that is, a string at a time) using string handling routines. This will make the program significantly faster. The approach I've used here is reasonable only for an introductory example. The conversion procedure's actual code, however, is complicated by the fact that it has to update the dialog box's progress bar. At each step of the conversion, a long integer variable with the current position in the file is incremented. This variable's value is used to compute the percentage of work completed, as you can see in the code above. The conversion procedure for removing symbols is very simple: while not Eof (FileIn) do begin Read (FileIn, Ch); if Ch < Chr (127) then Write (FileOut, Choose); ... The procedure used to capitalize the text, in contrast, is really a complex piece of code, which you can find in the code of the example. The conversion is based on a case statement with four branches:
Figure 12.2 shows an example of this code's effect; it shows a text file before and after the conversion. This program is far from adequate for professional use, but it is a first step toward building a full-scale case conversion program. Its biggest drawbacks are that it frequently converts proper nouns to lowercase, and capitalizes any letter after a period (even if it's the first letter of a filename extension). FIGURE 12.2: The result of running the Filter example's Capitalize conversion. Saving Generic DataIn addition to using text files, you can save other data types to a file, including integers, real numbers, arrays, and records. Using a custom file type instead of a text file may be an advantage because it might take less space (the textual representation of a number usually takes much more space than its binary value), but this approach won't let the user browse through the files using a text editor (which might be an advantage, too). How do you save a series of integers to a file? First you have to define a file as shown: SaveFile: file of Integer; Then you need to assign the real file to the file variable, open the file, operate on it , and close it. For example the following code saves all the numeric data collected in a 5x4 string grid: {save to the current file} AssignFile (SaveFile, CurrentFile); Rewrite (SaveFile); try {write the value of each grid element} for I := 1 to 5 do for J := 1 to 4 do begin Value := StrToIntDef (Trim ( StringGrid1.Cells [I, J]), 0); Write (SaveFile, Value); end; finally CloseFile (SaveFile); end; To save the data to a file, the program saves each value of the string grid (after converting it into a number). To accomplish this, the program uses two nested for loops to scan the grid. Notice the use of the temporary Value variable: the Write and Read procedures require a parameter passed by reference (var), so you cannot pass the property of a Delphi component, since it doesn't correspond directly to a memory location. Of course, the data should be read in the same order it is written, as you can see in the Open1Click method: {load from the current file} AssignFile (LoadFile, CurrentFile); Reset (LoadFile); try {read the value of each grid element} for I := 1 to 5 do for J := 1 to 4 do begin Read (LoadFile, Value); StringGrid1.Cells [I, J] := IntToStr(Value); end; finally CloseFile (LoadFile); end; From Files to StreamsAlthough direct handling of files, using the traditioanl Pascal-langauge appraoch is certainly still an interesting techcnique, a strongly urge you to use streams (the TStream and derived classes) to handle any complex files. Streams represent virtual files, which can be mapped to physical files, to a memory block, to a socket, or any other continuous series of bytes. You can find more on streams in the Delphi help file and in my Mastering Delphi book. ConclusionAt least for the moment, this chapter on files is the last of the book. Feel free to email me your comment and requests. If after this introduction on the Pascal language you want to delve into the object-oriented elements of Object Pascal in Delphi, you can refer to my published book Mastering Delphi 5 (Sybex, 1999). For more information on this and more advanced books of mine (and of other authors as well) you can refer to my web site, www.marcocantu.com. The same site hosts updated versions of this book, and its examples. Keep also an eye for the companion book Essential Delphi. Back to the Cover Page
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
© Copyright Marco Cantù, 1995-2020, All rights reserved |