DelphiBasics
  Home  |  Improving the second tutorial program
 Documents
 Tutorials

 Writing your first program
 Writing your second program
 Amending this program

 Delphi data types
   Numbers
   Text (strings and chars)
   Sets and enumerations
   Arrays
   Records

 Programming logic
   Looping
   SubRoutines
   Exception handling

 Dates and times

 Files

 Pointers

 Printing text and graphics

 Object Orientation basics
   Memory leaks!
   Inheritance
   Abstraction
   Interfaces
   An example class

 References

 Standard components

 Articles

 A brief history of Delphi

 Usability : file handling

 Usability : reference books

 Author links

  Improving the second tutorial program
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

 
 

Delphi Basics © Neil Moffatt All rights reserved.  |  Home Page