|
| Using object orientation |
| Take a look at the tutorials on object orientation to familiarise yourself on this powerful aspect of Delphi. It takes a while to get the concepts, but once learnt, you will never want to go back to purely procedural code. We will use object orientation to improve the second tutorial program. | |
| The new Stringy unit |
| In this tutorial, we will use a Unit that we defined in An example class tutorial. This unit is called Stringy and contains the definition of a Class called TStringy. Creating objects from this class allows us to perform involved string processing that is not available in the Delphi run time library. | |
| The class definition given below is the top (interface) part of the Stringy unit. As users of the class, we are not and should not be interested in the internal implementation (look at the other tutorial if you want to see the implementation). | |
| type // Define the new TStringy class TStringy = Class // These variables and methods are not visible outside this class // They are purely used in the implementation below // Note that variables are all prefixed bt 'st'. This allows us, for // example to use 'WordCount' as the property name - properties cannot // use the same name as a variable. private stText : String; // The string passed to the constructor stWordCount : Integer; // Internal count of words in the string stFindString : String; // The substring used by FindFirst/Next stFindPosition : Integer; // FindFirst/FindNext current position procedure GetWordCount; // Calculates the word count procedure SetText(const Value: String); // Changes the text string // These methods and properties are all usable by class instances published // Called when creating an instance (object) from this class // The passed string is the one operated on by the methods below constructor Create(Text : String); // Utility to replace all occurences of a substring in the string // The number of replacements is returned // This utility is CASE SENSITIVE function Replace(fromStr, toStr : String) : Integer; // Utility to find the first occurence of a substring in the string // The returned value is the found string position (strings start at 1) // if not found, -1 is returned // This utility is CASE SENSITIVE function FindFirst(search : String) : Integer; // Utility to find the next occurence of the FindFirst substring // if not found, -1 is returned // if no FindFirst performed before this call, -2 is returned // This utility is CASE SENSITIVE function FindNext : Integer; // The string itself - allow it to be read and overwritten property Text : String read stText write SetText; // We call a method to do this // The number of words in the document. Words are groups of characters // separated by blanks, tabs, carriage returns and line feeds property WordCount : Integer read stWordCount; end;
|
|
| With a TStringy object, we can get the word count of a string, replace all occurences of a substring in the string, and find one by one substrings in the string. The replace function is like the Delphi AnsiReplaceStr routine, but returns the count of replacements as an added bonus. This will prove useful. | |
| Using this unit and creating a TStringy object |
| Before we can create a TStringy variable, we must reference the new Stringy unit: | |
| uses SysUtils, StrUtils, Forms, Dialogs, Classes, Controls, StdCtrls, Stringy; // Uses our new TStringy class unit
|
|
| We will use the new TStringy object in the CorrectButtonClick method of the second tutorial program: | |
| procedure TForm1.CorrectButtonClick(Sender: TObject); var changeCounts : array[TEH..EHT] of Integer; fullText : TStringy; // Our new TStringy variable begin fullText := TStringy.Create(fileData.Text); ...
|
|
| After defining a TStringy variable, we must use the class type to construct an object. Do not try to use Create on the variable - it is null until instantiated, which is what the Create does. | |
| When we do the create, we pass the full text of the user file as a parameter. This will be held internally by the object, allowing us to perform utility functions on it at our leisure. | |
| Using this TStringy object |
| We will use TSTringy to change all occurences of mis-spellings of the word 'the', and display the change counts on the Unit form. First let us do the changes: | |
| // Change the 3 chosen basic ways of mis-spelling 'the' changeCounts[TEH] := fullText.Replace('Teh','The') + fullText.Replace('teh','the'); changeCounts[ETH] := fullText.Replace('eth','the'); changeCounts[EHT] := fullText.Replace('eht','the');
|
|
| Here we have used the TStringy Replace function to do the changes. It returns counts of the changes it has made. Neat. It has made the changes on its copy of the file text remember. We must extract that version now: | |
| // Store the changed text back into the string list fileData.Text := fullText.Text; // And redisplay this string list MemoBox.Text := fileData.Text;
|
|
| And we must display the word change statistics: | |
| if changeCounts[TEH] = 1 then Label1.Caption := 'Teh/teh changed once' else Label1.Caption := 'Teh/teh changed '+ IntToStr(changeCounts[TEH])+' times'; if changeCounts[ETH] = 1 then Label2.Caption := 'eth changed once' else Label2.Caption := 'eth changed '+ IntToStr(changeCounts[ETH])+' times'; if changeCounts[EHT] = 1 then Label3.Caption := 'eht changed once' else Label3.Caption := 'eht changed '+ IntToStr(changeCounts[EHT])+' times';
|
|
| And finally, we display the count of words in the file: | |
| Label4.Caption := 'There are '+IntToStr(fullText.WordCount)+ ' words in the file';
|
|
| Putting it all together |
| Here is the full revised tutorial 2 code, and sample screen text: | |
| // Full Unit code. // ----------------------------------------------------------- // We now use the TStringy class to simplify the coding and // improve the display of word change statistics. unit Unit1; interface uses SysUtils, StrUtils, Forms, Dialogs, Classes, Controls, StdCtrls, Stringy; // Uses our new TStringy class unit type TheMisSpelled = (TEH, ETH, EHT); // Enumeration of 'the' miss-spellings TForm1 = class(TForm) // Visual objects inserted by Delphi LoadButton : TButton; SaveButton : TButton; CorrectButton : TButton; MemoBox : TMemo; Label1 : TLabel; Label2 : TLabel; Label3 : TLabel; Label4 : TLabel; // Methods added by Delphi procedure LoadButtonClick(Sender: TObject); procedure SaveButtonClick(Sender: TObject); procedure CorrectButtonClick(Sender: TObject); private published // Constructor added by Delphi procedure FormCreate(Sender: TObject); end; var // Global definitions in our unit Form1: TForm1; fileName : String; fileData : TStringList; openDialog : TOpenDialog; implementation {$R *.dfm} // Include form definitions // Procedure called when the main program Form is created procedure TForm1.FormCreate(Sender: TObject); begin // Set the title of the form - our application title Form1.Caption := 'Very simple spell corrector'; // Disable all except the load file button SaveButton.Enabled := false; CorrectButton.Enabled := false; // Clear the file display box MemoBox.Clear; // Enable scroll bars for this memo box MemoBox.ScrollBars := ssBoth; // do not allow the user to directly type into the displayed file text MemoBox.ReadOnly := true; // Set the font of the memo box to a mono-spaced one to ease reading MemoBox.Font.Name := 'Courier New'; // Set all of the labels to blank Label1.Caption := ''; Label2.Caption := ''; Label3.Caption := ''; Label4.Caption := ''; // Create the open dialog object - used by the GetTextFile routine openDialog := TOpenDialog.Create(self); // Ask for only files that exist openDialog.Options := [ofFileMustExist]; // Ask only for text files openDialog.Filter := 'Text files|*.txt'; // Create the string list object that holds the file contents fileData := TStringList.Create; end; // Procedure called when the file load button is pressed procedure TForm1.LoadButtonClick(Sender: TObject); begin // Display the file selection dialog if openDialog.Execute then // Did the user select a file? begin // Save the file name fileName := openDialog.FileName; // Now that we have a file loaded, enable the text correction button CorrectButton.Enabled := true; // Load the file into our string list fileData.LoadFromFile(fileName); end; // And display the file in the file display box MemoBox.Text := fileData.Text; // Clear the changed lines information Label1.Caption := ''; Label2.Caption := ''; Label3.Caption := ''; // Display the number of lines in the file Label4.Caption := fileName+' has '+IntToStr(fileData.Count)+' lines of text'; end; // Procedure called when the file save button is pressed procedure TForm1.SaveButtonClick(Sender: TObject); begin // Simply save the contents of the file string list if fileName <> '' then fileData.SaveToFile(fileName); // And disable the file save button SaveButton.Enabled := false; end; // Procedure called when the correct text button is pressed procedure TForm1.CorrectButtonClick(Sender: TObject); var changeCounts : array[TEH..EHT] of Integer; fullText : TStringy; // Our new TStringy variable begin // Now we record counts of changed words rather than changed lines. // Process the whole file as a single string // First we create a TStringy object for processing on. We pass to it // the full file text as a string. fullText := TStringy.Create(fileData.Text); // Change the 3 chosen basic ways of mis-spelling 'the' changeCounts[TEH] := fullText.Replace('Teh','The') + fullText.Replace('teh','the'); changeCounts[ETH] := fullText.Replace('eth','the'); changeCounts[EHT] := fullText.Replace('eht','the'); // Store the changed text back into the string list fileData.Text := fullText.Text; // And redisplay this string list MemoBox.Text := fileData.Text; // Display the word change totals if changeCounts[TEH] = 1 then Label1.Caption := 'Teh/teh changed once' else Label1.Caption := 'Teh/teh changed '+ IntToStr(changeCounts[TEH])+' times'; if changeCounts[ETH] = 1 then Label2.Caption := 'eth changed once' else Label2.Caption := 'eth changed '+ IntToStr(changeCounts[ETH])+' times'; if changeCounts[EHT] = 1 then Label3.Caption := 'eht changed once' else Label3.Caption := 'eht changed '+ IntToStr(changeCounts[EHT])+' times'; // Finally, display the number of words in the file Label4.Caption := 'There are '+IntToStr(fullText.WordCount)+ ' words in the file'; // Finally, indicate that the file is now eligible for saving SaveButton.Enabled := true; // And that no more corrections are necessary CorrectButton.Enabled := false; // Finally, free the TStringy object fullText.Free; end; end.
|
|
| The displayed file before correction |
| Teh cat sat on eth mat The cat did not sit on eth mat or teh floor Teh teh teh eth eth eht eht Final line of 5.
|
|
| The displayed file after correction |
| The cat sat on the mat The cat did not sit on the mat or the floor The the the the the the the Final line of 5.
|
|
| The displayed statistics |
| Teh/teh changed 5 times eth changed 4 times eht changed 2 times There are 28 words in the file
|
|
| | | | |