STRINGS (CHARACTER ARRAYS) AND SLICES


1. STRINGS

A further type of array is the string. A string is a sequence of characters enclosed (in Ada) in double quotes. Strings are a special type of array referred to as a character arrays. As such we can use standard array operations on strings. For example, we can access any character in a string using an index.

Ada provides a specific predefined data type for character arrays called STRING:

DATA_ITEM_NAME : STRING (N..M);

Where N and M are integers defining the lower and upper bounds for the string/character array. (Note the similarity with array declarations.)

2. OPERATIONS ON STRINGS

We have seen that Ada supports a number of predefined operations on arrays.

catenationS1&S2
comparisonS1=S2
copyS1:=S2

Which may equally be applied to strings. The concept of slices is also supported. Example program:

-- STRING EXAMPLE
-- 29 September 1997
-- Frans Coenen
-- Dept Computer Science, University of Liverpool

with TEXT_IO ;
use TEXT_IO ;

procedure STRING_EXAMPLE is
        NAME1: constant STRING(1..5):= "Frans";
        NAME2: STRING(1..15);
begin
-- Assignment
        NAME2(1..9):= "P. Coenen";
-- Output
        PUT("Name = ");
        PUT(NAME1);
        PUT(" ");
        PUT(NAME2(1..9));
        NEW_LINE;
-- Output individual elements
        PUT("Initials = ");
        PUT(NAME1(1));
        PUT(". ");
-- Output slices
        PUT(NAME2(1..4));
        PUT(".");
        NEW_LINE;
end STRING_EXAMPLE ;

The output from the above will be as follows.

kuban-378 string_example
Name = Frans P. Coenen
Initials = F. P. C.

4. EXAMPLE PROBLEM PASSWORD VERIFICATION


4.1. Requirements

Design a Ada program that takes a text string as input, analysis this text string and outputs whether it is a valid password or not (a similar operation, at least in part, to that performed by the passwd UNIX command). A valid password displays the following attributes:

If the string is not a valid password output the reason why. Note the following ASCII equivalents:

ASCII CODECHARACTER
33-47, 58-64, 91-96, 123-126Special characters
48-57Numeric characters
65-90Upper case letters
97-122Lower case letters

4.2. Design

A top-down analysis of the proposed problem is given below.

TOP DOWN ANALYSIS

We will implement this using four procedures/functions:

  1. PASSWORD (Top level procedure): Input string, calls to CHECK_LENGTH, CHECK_CHARACTERS and CHECK_OTHERS. Output "valid password" if functions all return TRUE.
    NAMEUSAGETYPERANGE
    INPUT_STRINGInput variableSTRINGUnconstrained
    STRING_LENGTHGlobal variableINTEGERDefault
    VALID_FLAGGlobal variableBOOLEANTRUE or FALSE
  2. CHECK_LENGTH (level 2 function): Check string length. Return TRUE if OK, FALSE otherwise.
    NAMEUSAGETYPERANGE
    LENGTHFormal parameterINTEGERDefault
  3. CHECK_CHARACTERS (Level 2 function) : Check number of characters. Return TRUE if OK, FALSE otherwise.
    NAMEUSAGETYPERANGE
    PS_WORDFormal parameterSTRINGUnconstrained
    LENGTHFormal parameterINTEGERDefault
    CODELocal variableINTEGERDefault
    COUNTERLocal variableINTEGERDefault
  4. CHECK_OTHERS (level 2 function): Check for numeric or special character. Return TRUE if OK, FALSE otherwise.
    NAMEUSAGETYPERANGE
    PS_WORDFormal parameterSTRINGUnconstrained
    LENGTHFormal parameterINTEGERDefault
    CODELocal variableINTEGERDefault

Complete Nassi-Shneiderman charts for the above are given below:

NASSI_SHNEIDERMAN CHART

NASSI_SHNEIDERMAN CHART

A flow diagram/chart indicating the broad flow of control through the above is given below.

NASSI_SCHNEIDERMAN CHART

4.3. Implementation

-- PASSWORD
-- 1 October 1997
-- Frans Coenen
-- Dept Computer Science, University of Liverpool

with TEXT_IO;
use TEXT_IO;

procedure PASSWORD is
        INPUT_STRING  : STRING(1..20);
        STRING_LENGTH : INTEGER;
        VALID_FLAG    : BOOLEAN := TRUE;

        --------------------------------------------------------------------------
        -- CHECK LENGTH
        function CHECK_LENGTH(LENGTH: INTEGER) return BOOLEAN is
        begin
                if (LENGTH >= 6 and LENGTH <= 8) then
                        RETURN(TRUE);
                else
                        PUT_LINE("Password must comprise 6 to 8 characters");
                        RETURN(FALSE);
                end if;
        end CHECK_LENGTH;
        --------------------------------------------------------------------------
        -- CHECK CHARACTERS
        function CHECK_CHARACTERS(PS_WORD: STRING; LENGTH: INTEGER) return
                        BOOLEAN is
                COUNTER     : INTEGER := 0;
                CODE        : INTEGER;
        begin
                for LOOP_PARAMETER in 1..LENGTH loop
                        CODE := CHARACTER'POS(PS_WORD(LOOP_PARAMETER));
                        if (CODE >= 65 and CODE <= 90) or
                                        (CODE >= 97 and CODE <= 122) then
                                COUNTER := COUNTER+1;
                                if COUNTER = 2 then
                                        RETURN(TRUE);
                                end if;
                        end if;
                end loop;
                PUT("Password must comprise at least two alphabetic ");
                PUT_LINE("characters");
                RETURN(FALSE);
        end CHECK_CHARACTERS;
        --------------------------------------------------------------------------
        -- CHECK OTHERS
        function CHECK_OTHERS(PS_WORD: STRING; LENGTH: INTEGER) return
                        BOOLEAN is
                CODE        : INTEGER;
        begin
                for LOOP_PARAMETER in 1..LENGTH loop
                        CODE := CHARACTER'POS(PS_WORD(LOOP_PARAMETER));
                        if (CODE >= 33 and CODE <= 64) or
                                        (CODE >= 91 and CODE <= 96) or
                                        (CODE >= 123 and CODE <= 126) then
                                RETURN(TRUE);
                        end if;
                end loop;
                PUT("Password must comprise at least one numeric or ");
                PUT_LINE("special character");
                RETURN(FALSE);
        end CHECK_OTHERS;
        --------------------------------------------------------------------------

begin
        PUT_LINE("Input a proposed password");
        GET_LINE(INPUT_STRING,STRING_LENGTH);
-- Check length
        if not CHECK_LENGTH(STRING_LENGTH) then
                VALID_FLAG := FALSE;
        end if;
-- Check number of alphabetic characters
        if not CHECK_CHARACTERS(INPUT_STRING,STRING_LENGTH) then
                VALID_FLAG := FALSE;
        end if;
-- Check number of other characters
        if not CHECK_OTHERS(INPUT_STRING,STRING_LENGTH) then
                VALID_FLAG := FALSE;
        end if;
-- Output result
        if VALID_FLAG then
                PUT_LINE("Valid password");
        else
                PUT_LINE("Invalid password");
        end if;
end PASSWORD;

Note how we have included the ability to type in strings of any length between 1 and 20 and how string types are passed to procedures/functions.


4.4. Testing

TEST CASEEXPECTED RESULT
INPUT_STRINGOUTPUT
aVeryLongPasswordIndeedinvalid
ainvalid
password_With_20Charinvalid
myPass1valid

4.4.1. Black Box Testing

Input Testing: The input string variable should be tested at its limits, somewhere in middle and outside the limits as indicated by the table to the right.

4.4.2. White Box Testing

Path Testing: The flow chart given above identifies the paths through the system. Test cases should be generated to ensure that every path is exercised. The black box test cases given above right will achieve this. However, selection of paths is dependent on a number of compound Boolean expressions some of which are extremely complicated.

AB
TT
TF
FT
FF

Commencing with the CHECK_LENGTH function this incorporates a Boolean expression of the form A and B giving a standard truth table of the form shown to the right. Of course the nature of the variable to be tested (STING_LENGTH) is such that at least one or other of A and B must be true (the string length cannot be both less than 6 and greater than 8 at the same time). Consequently the following set of test cases will be appropriate.

TEST CASEEXPECTED RESULT
INPUT_STRINGOutput
word777 (STRING_LENGTH 7)valid
word99999 (STRING_LENGTH 9)invalid
word5 (STRING_LENGTH 5)invalid

TEST CASEEXPECTED RESULT
ASCII CODEINPUT_STRINGOutput
60<invalid
65Ainvalid
78Ninvalid
90Zinvalid
95_invalid
97ainvalid
110ninvalid
122zinvalid
130}invalid

The CHECK_CHARACTER function includes a compound Boolean expression of the form (A and B) or (C and D). Taking all possible combinations into account this would produce a 16 line truth table, i.e. 16 test cases. However, we can reduce the number of test cases given that the data item to be tested is a value in a sequence (0 - 127). In fact (A and B) and (C and D) describe ranges of values. Therefore, in this case it is appropriate to derive tests that cause the selection expression to be tested with the ASCII character codes: 60, 65, 78, 90, 95, 97, 110, 122 and 130, as shown in the table (above right). We should also test the situation where no alphabetic characters occur, one occurs, two occurs and more than two occurs.

A similar approach can be used to test the CHECK_OTHERS function. A suitable set of test cases is given below. Note that due to difficulty inputting ASCII character 127 a test above 126 has not been included.

TEST CASEEXPECTED RESULT
ASCII CODEINPUT_STRINGOutput
32(space)invalid
33!invalid
502invalid
64@invalid
78Ninvalid
91[invalid
94^invalid
96`invalid
110ninvalid
123{invalid
124|invalid
126~invalid

Example Problem Password Verification.




Created and maintained by Frans Coenen. Last updated 11 October 1999