TURBO PASCAL STRINGS

Strings are not a supported data type of standard Pascal, and so we must reply on the implementation of string handling afforded with various versions of Pascal. In this course, we use the string definitions and manipulation in Borland Turbo Pascal V7.0.

Strings are arrays of characters of a specified maximum length. Strings are structured types; that is, they can be broken down into their individual components. Strings in Turbo Pascal V7 have a default length of 255 and are declared by the keyword String; otherwise, their maximum length must be declared when they are defined. Strings may grow dynamically from length 0 (= "empty string" or "null string") to an absolute maximum of length 255. Examples:

Type
  File_name = String[14];          { max length = 14 }
  Employee_name = String[30];

Var
  Infile_name: File_name;
  Emp_name: Employee_name;
  Line: String;                    { max length = 255 }
The READ and WRITE procedures support strings, so that (using the above definitions), Read(Emp_name) and Writeln(Infile_name) are legal Pascal statements.

There are many methods for modifying and comparing strings:

  1. STRING ASSIGNMENT
    SYMBOL: :=

    Strings may be assigned values using the := operator.
    Example: Infile_name := 'A:\PROG006.OUT'; Note that if we attempted to make the following assignment: Infile_name :='A:\OUTPUT\PROG006.OUT', the value assigned to Infile_name would be 'A:\OUTPUT\PROG' which may not be what we expected! Also, Infile_name[2] = ':' and Infile_name[11] = 'P'.

  2. STRING CONCATENATION ("GLUE")
    SYMBOL: +

    Example: Infile_name := 'A:\' + 'PROG006' + '.OUT';

  3. INDIVIDUAL STRING ELEMENTS
    SYMBOL: St[n]

    Example: Writeln(Infile_name[3]);
    causes the third character of Infile_name to be printed.

  4. STRING COMPARISON
    SYMBOLS: =, <, <=, >, >=, <>

    If X and Y are strings, then X r Y is either TRUE or FALSE where r is any of the six relational characters listed above. X = Y is true if and only if their lengths are equal and X[i] = Y[i] for all i = 1, 2, .... When X and Y are compared, X[i] and Y[i] are compared in sequence according to their ASCII ordinal values. If the strings are of different length but equal character by character up to and including the last character of the shorter string, then the shorter string is considered smaller. Examples:

         'A' < 'B'           TRUE
         'Z' < 'a'           TRUE
         'a' < 'B'           FALSE
         'TURBO' ='TURBO'    TRUE
         'TURBO' = 'Turbo'   FALSE
         'turbo ' = 'turbo'  FALSE
         'turbo ' > 'turbo'  TRUE
    

    STRING FUNCTIONS

  5. LENGTH
    Syntax: Length(St)

    Returns the current length of the string St in range 0..255.

    Example:

              Var
                St: String[20];  Len: Integer;
    
                St := '';              { make St the null string }
                Len := Length(St);     { Len now has value 0 }
                St := St + 'Begin';
                Len := Length(St);     { Len now has value 5 }
    
  6. POSITION
    Syntax: Pos(Pattern, Target)

    The Pos function scans the string Target to find the first occurrence of the string Pattern in the string Target. The value returned by Pos is an integer indicating this position. When the string Pattern is not found in Target, Pos returns the value zero.

    Example:

         Var Target, Pattern: String[20]; 
         Location: Integer;
    
         Target := 'MISSISSIPPI';
         Pattern := 'IS';
    
         Location := Pos(Pattern, Target);  { Location = 2 }
    
    If Pattern = 'SIP', then Location = 7.
    If Pattern = 'is', then Location = 0.
    If Pattern = ' ', then Location = 0.

  7. COPY
    Syntax: Copy(St, Start, Number)

    The Copy function returns a substring copied out of St starting at position Start containing Number characters. Thus St is a string expression and Start and Number are positive integers. If Start > Length(St), the empty string is returned. If Start + Number > Length(St), only characters in the string St are returned. If Start is outside the range 1..255, a run-time error occurs.

    Example:

         Var  St, Part: String[20];
              Start, Number: Integer;
    
         St := 'MISSISSIPPI';
         Start := 5;
         Number := 3;
         Part := Copy(St, Start, Number);  { Part = 'ISS' }
    
    If Start = 12, then Part = ''.
    If Start = 5 and Number = 20, then Part = 'ISSIPPI'.

    STRING PROCEDURES

  8. DELETE
    Syntax: Delete(St, Start, Number)

    This procedure will alter St by removing Number characters from St beginning at position Start. If Start > Length(St), no characters will be removed from St. If Start + Number > Length(St), only characters in the string will be removed.

    Example (using above defined storage):

         St := 'MISSISSIPPI';
         Start := 5;
         Number := 4;
         Delete(St, Start, Number); { St = 'MISSPPI' }
    
    If Start = 15, St does not change.
    If Start = 2 and Number = 20, then
    Delete(St, Start, Number) changes St to 'M'.

  9. INSERT
    Syntax: Insert(Pattern, Target, Position)

    Insert actually places a copy of the string Pattern into the string Target to the left of the character at location Position. Thus Target will be altered by Insert. If Position > Length(Target), Pattern will be concatenated to the end of Target to the extent possible.

    Example (using above defined storage):

         Target := 'MISSISSIPPI';
         Part := 'xyz';
         Insert(Part, Target, 5); { Target = 'MISSxyzISSIPPI' }
    
    If Part = 'abcdefghijkl' and Position = 7, then
    Insert(Part, Target, Position)
    changes Target to 'MISSISabcdefghijklSI'.
    If Position = 70, then Target = 'MISSISSIPPIabcdefghi'.

  10. VAL
    Syntax: VAL(St, Num, Error_code)

    Purpose: to convert a string value which is in the form of a valid numeric quantity to the numeric value it represents. If the conversion can be made without error, the value is placed in Num (and the conversion is controlled by the declared numeric type of Num) and Error_code = 0. If an error occurs, the value of Num is unchanged; Error_code > 0 and points to the first position in St where a conversion error occurred. Examples:

         Var
           Num_str: String[20];
           R_Num: Real;
           I_num, Error: Integer;
    
         Num_str := '23.45';
         Val(Num_str, R_Num, Error); {R_Num = 23.45, Error = 0}
         Val(Num_str, I_Num, Error); {Error = 3; R_Num stays 23.45 }
         Num_str := '256,78';
         Val(Num_str, R_Num, Error); {R_Num = 23.45, Error = 4}
         Num_str := '23456';
         Val(Num_str, I_Num, Error); {I_Num = 23456, Error = 0}
         Val(Num_str, R_Num, Error); {R_Num = 23456.0, Error = 0}
    
  11. STR
    Syntax: STR(Numeric, St)

    Purpose: to perform the inverse function of VAL; that is, to receive a numeric value and convert it (according to any formatting information) to a string.

    Examples (using the storage defined in 10);
    Result is String[20]:

         I_Num := -345;
         Str(I_num, Result);           {Result = '-345'}
         R_num := 2345.678;
         Str(R_num :0:6, Result);      {Result = '2345.678000'}
         Str(R_num :0:1, Result);      {Result = '2345.7'}
         Str(R_num :10:2, Result);     {Result = '   2345.68'}
         Str(R_num, Result);           {Result = ' 2.3456780000E+03'}
    

EXAMPLES OF THE USE OF THESE FUNCTIONS AND PROCEDURES:

Const
  Space = ' ';

Var
  St, First, Last: String[80];
  Position, Len: Integer;
  Remove: Char;
  1. Remove all lead blanks from a string
    
    St := '     now is the time';
    While POS(Space, St) = 1 Do
      Delete(St, 1, 1);
    
  2. Reverse the order of names in 'Smith John'
    
    St := 'Smith John';
    Location := POS(Space, St);        { Location = 6 }
    Len := Length(St);                 { Len = 10 }
    Last := COPY(St, 1, Location - 1); { Last = 'SMITH'}
    First := COPY(St, Location + 1, Len - Location);
    St := First + Space + Last;
    
  3. Remove all specified characters from a string
    
    St := 'Txxhixs xsenxtxxencxe hxxaxs toxxox xmaxxny exxexes.';
    Remove := 'x';
    New := '';
    For Location := 1 to Length(St) Do
      If St[Location] <> Remove
        Then New := New + St[Location];
    
    What's wrong with using a loop like this:
    
    For Location := 1 to Length(St) Do
      If St[Location] = Remove
        Then Delete(St, Location, 1);
    
    Answer: will only remove isolated single 'x's in string

  4. Insert a blank every fifth character in a string
    
    Const
      Blank = ' ';
    
    Var
      St, New: String[80];
      Location, Mid: 1..80;
    
    New := '';  {New is the null string }
    For Location := 1 to Length(St) Do
      If Location MOD 5 = 0
        Then New := New + St[Location] + Blank
        Else New := New + St[Location];
    
  5. Build a string from null, reverse to that of a given string
    
    New := '';
    For Location := Length(St) downto 1 Do
      New := New + St[Location];
    
  6. Count number of vowels in a string (use set statement)
    
    Var
      St, New: String[80];
      Location, Mid: 1..80;
      Count: 0..80;
      Valid_Ch: Set of Char;
    
    Count := 0;
    Valid_Ch := ['A', 'E', 'I', 'O', 'U', 'a', 'e', 'i', 'o', 'u'];
    For Location := 1 to Length(St) Do
      If St[Location] IN Valid_Ch
        Then Count := SUCC(Count);
    Writeln('There were ', Count, ' vowels in ', St);
    
  7. Convert all lower case letters in a string to upper case and visa-versa!
    
    { convert lower case letters to upper case }
    For Location := 1 to Length(St) Do
      St[Location] := UPCASE(St[Location]);
    
    { convert upper case letters to lower case }
    Valid_Ch := ['A'..'Z'];
    For Location := 1 to Length(St) Do
      If St[Location] IN Valid_Ch
        Then St[Location] := CHR(ORD(St[Location]) + 32);
    
    { Exercise: write a single structure which will convert upper
      case letters to lower case AND visa-versa }
    
  8. Remove all the 'a', 'b' and 'c' from a given string:
    
    Type
      Bad_letters = Set of 'A'..'z';
    
    Var
      Remove: Bad_letters;
      Good_string: String[80];
      LCV: Integer;
    
    Remove := ['a', 'b', 'c'];
    Readln(X);
    Good_string := '';
    For LCV := 1 to Len(X) Do
      If NOT(X[LC] IN Remove)
        Then Good_string := Good_string + X[LCV];
    
  9. Receive a real value; convert it to another real value with the integer and decimal parts interchanged:
    
    Const
      Blank = ' ';
      Dec_pt = '.';
      Char_Zero = '0';
    
    Var
      St, New: String[80];
      Location, Mid: 1..80;
      Valid_Ch: Set of Char;
      Len, Error: Integer;
      First_real, Second_real: Real;
      Real_string, Int_string, Dec_string: String[30];
    
      Readln(First_real);
      STR(First_real :0:8, Real_string);
      Location := POS(Dec_pt, Real_string);
      Len := Length(Real_string);
      Int_string := COPY(Real_string, 1, Location - 1);
      Dec_string := COPY(Real_string, Location + 1, Len - Location);
    
      Location := Length(Dec_string);       { strip trailing zeros }
      While Dec_String[Location] = Char_Zero Do
        Location := PRED(Location);
    
      { adjust the location marker if decimal string is all 0s }
      If Location = 0
        Then Location := 1;
    
      Dec_string := COPY(Dec_string, 1, Location);
      Real_string := Dec_string + Dec_pt + Int_string;
      VAL(Real_string, Second_real, Error);
    
  10. Separate out pieces of a string from each other and process these pieces (they could be integer values, etc.) This is an example of a general problem called parsing a string.

    1. Receive a string of integers separated by slashes (possibly with leading/trailing spaces); extract the numbers, print them one per line, and calculate their sum; no "field" is empty:
      
      Const
        Field_separator = '/';
        Blank = ' ';
      
      Var
        St: String[80];
        Int_string: String[30];
        Value, Sum: Integer;
        Location: 1..80;
        Len, Error: 0..80;
        
      
      St := St + Field_separator;  { add mark to terminate last field }
      Sum := 0
      While Length(St) <> 0 Do
        Begin
      
      
          { extract one field }
      
          Location := POS(Field_separator, St);
          Int_string := COPY(St, 1, Location - 1);
          DELETE(St, 1, Location);
      
      
          { trim leading/trailing spaces }
      
          Location := 1;
          While Int_string[Location] = Blank Do
            Location := SUCC(Location);
          DELETE(Int_string, 1, Location - 1);
          Len := Length(Int_string);
          Location := Len;
          While Int_string[Location] = Blank Do
            Location := PRED(Location);
          DELETE(Int_string, Location + 1, Len - Location);
      
      
          { convert to numeric type, print and add }
      
          VAL(Int_string, Value, Error);
          Writeln(Value :4);
          Sum := Sum + Value
        End;
        Writeln('----');
        Writeln(Sum :4)
      
      What can go wrong here? Empty fields can cause runaway string operations if we are not careful about checking for unusual cases. See an example.

    2. Same as (a), except allow "empty" fields, and leading/trailing slashes:
      
      Const
        Field_separator = '/';
        Blank = ' ';
      
      Var
        St: String[80];
        Int_string: String[30];
        Value, Sum: Integer;
        Location: 1..80;
        Len, Error: 0..80;
        
      
      St := St + Field_separator;  { add mark to terminate last field }
      Sum := 0;
      While Length(St) <> 0 Do
        Begin
      
      
          { extract one field }
      
          Location := POS(Field_separator, St);
          Int_string := COPY(St, 1, Location - 1); { Int_string could be null }
          DELETE(St, 1, Location);
      
      
          { trim leading/trailing spaces }
      
          Len := Length(Int_string);
          Location := 1;
          While (Int_string[Location] = Blank) and (Location < Len) Do
            Location := SUCC(Location);
          DELETE(Int_string, 1, Location - 1);
          Len := Length(Int_string);
          Location := Len;
          While (Int_string[Location] = Blank) and (Location > 0) Do
            Location := PRED(Location);
          DELETE(Int_string, Location + 1, Len - Location);
      
      
          { convert to numeric type, print and add }
      
          VAL(Int_string, Value, Error);
          If Error = 0
            Then
              Begin
                Writeln(Value :4);
                Sum := Sum + Value
              End
        End;
        Writeln('----');
        Writeln(Sum :4)
      
    3. Same as (b), except handle "dirty" fields with non-blanks before/after the numbers, as well as invalid data:
      
      Program Parse;
      
      { This program demonstrates how to "parse" a string and separate out the
        important data values contained in it.  It is assumed that Line is a
        string which contains integer values separated by /.  Typical strings
        might be   34 / 45 / 789  or  /  / 3  / 48/  79/  The objective of the
        program is to separate out all the individual integer values from the
        string and process them (for example, add them up).
      
        The basic idea behind the processing is to form a WHILE loop controlled
        by the length of the string.  The string will contract as the loop runs
        because pieces will be pulled from the front of the string.
      
        While length string > 0 Do
          1.  delete all lead spaces in string
          2.  find the location of the first / in the string
          3.  copy out from the string all characters from the beginning of
              the string up to and including /  -- store this chunk of the
              string in "Piece"
          4.  delete the characters in Piece from the original string
          5.  counting backwards from the end of Piece, find the last digit
              in Piece (if there indeed is one)
          6.  copy out of Piece the portion of Piece which contains only
              digits (or possibly embedded non-digits)
          7.  convert this string to a numeric value using VAL
          8.  if the conversion is successful, print this value and add it
              to Sum
      
          After the loop quits, print the sum of all the numeric values
          successfully converted from the original string }
      
      Uses
        crt;
      
      Const
        Line = '     /    23    /      478   /8/  8z7  /  2.3 /xvt/ 987 /  5996   ';
        Sep = '/';
        Space = ' ';
        Digits: Set of Char = ['0'..'9'];
      
      Var
        X: String;
        N, Error, Sum: Integer;
        Piece: String;
        Loc: Integer;
      
      Begin
        Clrscr;
      
        X := Line;
        X := X + Sep;  { Ensure that X ends with a / }
        Sum := 0;
      
        While Length(X) > 0 Do
          Begin
      
      
            { Delete the lead spaces from the string   X }
      
            While Pos(Space, X) = 1 Do
              Delete(X, 1, 1);
      
      
            { Delete any lead non-digit characters.  Yes, this would also 
              delete lead spaces - but this is only an example!           }
      
            While NOT(X[1] in Digits) AND (Length(X) >= 1) Do
              Delete(X, 1, 1);
      
      
            { Find the location of the first / in X; copy all characters
              in X from the beginning of X up to and including the  /  into
              the string Piece; delete these characters from the beginning of  X }
      
            Loc := Pos(Sep, X);
            Piece := COPY(X, 1, Loc);
            Delete(X, 1, Loc);
      
      
            { Count backwards from the end of Piece until a digit is found }
      
            While NOT(Piece[Loc] in Digits) AND (Loc > 0) Do
              Loc := Loc - 1;
      
      
            { Make a new copy of Piece which consists of characters from the
              beginning of Piece to the place where a digit was located; it's
              possible that Loc = 0 if no digits were encountered in Piece
              (for example, if the field consists only of spaces or of non-
              digit characters)  }
      
            Piece := COPY(Piece, 1, Loc);
      
      
            { The following code can be used to see how Piece is formed for
              each field separated by /
      
            If Length(Piece) > 0
              Then Writeln('piece = ''', piece, '''')
              Else Writeln('piece = null');  }
      
      
            { Convert Piece to an integer value N }
      
            VAL(Piece, N, Error);
      
      
            { If Piece is null or contains garbage, ignore  N (that is, when
              Error > 0); otherwise, add  N  to  Sum  and print N }
      
            If Error = 0
              Then
                Begin
                  Writeln(N :4);
                  Sum := Sum + N
                End;
      
          End;  { WHILE }
      
          Writeln('----');
          Writeln(Sum :4);
          Readln;
      End.