Section 6 - A Nice Example

4A.6.1 Choosing a Password: The Elements

So how hard is it, anyway, to write a program that gets a password selection from the user?  It's easy, but not short.  Let's look at it in steps.  Assume that the user enters the password candidate into a string variable, password.   This is done in an input filter loop that keeps getting a new candidate password until the user enters a legal password string.  We assume that there is a larger while loop that we don't see in these code fragments.  The while loop I am speaking of is the loop that continues to get another password candidate from the user until it passes all of our tests for validity.

  1. The password has to be a certain length.  

    For this we use the length() method of the string class.  It tells us the length of the string that is used to call it
    length = password.length();
    
    By storing the return value of the length() method in an int variable, length, we can reuse this number without re-invoking the length() method later in the code.  This is a common practice whenever we use the return value of a function several times in a program.
    // test for reasonable length
    if (length < 6 || length > 15)
    {
    cout << "Password must be between 6 and 15 characters.\n";
    continue;
    }
    
    In the above code fragment, we are assuming that we are inside a while loop that keeps going until the password is validated. The use of continue tell us we are going to start the while loop again, getting a new password candidate from the user.
  2. The user may want to quit.

    If the user types a 'q' or 'Q' then they want to quit without typing a password.
    // test for quit first
    if (
    length == 1
    &&
    ( password[0] == 'q'  || password[0] == 'Q')
    )
    {
    cout << "No password defined.\n";
    break;  // from loop
    }
    
    This tests the condition that the user typed one letter and that was a 'q' (or 'Q'). Make sure you understand the logic here. If the test passes, then we know the user wants out, so we break from the input filter while loop.
  3. We examine each letter one-at-a-time in a for loop.
     
     We have to look at every char in this string. That involves a for loop.  In each pass through the for loop we examine a character and see if it is a letter (between 'a' and 'z', or between 'A' and 'Z') or a numeral (between '0' and '9').  Note that single quotes are needed here since we are comparing each letter to the ASCII codes of the characters or numerals.  Here is the loop that performs this test.
    validated = true;   // assume innocent entering loop
    // allow only letters and numbers
    for (int k = 0; k < length; k++)
    {
    letter = password[k];   // store in local variable so we can reuse
    if (letter >= 'a' && letter <= 'z')
    continue; //  the for loop, we have a good lower case letter
    else if (letter >= 'A' && letter <= 'Z')
    continue; //  the for loop, we have a good upper case letter
    else if (letter >= '0' && letter <= '9')
    continue; //  the for loop, we have a good numeral
    else
    {
    // if we fell through, this letter is not one of the three legal types
    cout << "Use only A-Z, a-z or 0-9, please.";
    validated = false;
    break;   // from the for loop leaving validated as false
    }
    }
    
    An important note here is the meaning of the break and the continue.  This for loop is inside a larger while loop (which we don't see, because I have taken this out-of-context).  So when we encounter a break inside a loop, inside a larger loop, which loop are we breaking?  The answer is the same in all languages:  the innermost loop.  So the break and continue apply to this for loop, not the larger while loop.  We see here that we are examining each character in the candidate password.  As a common practice, we read the current char into a local variable, letter, and test letter.  This is in place of re-invoking password[k] each time we need the letter. The method above is more efficient.
tree by lake

4A.6.2 The Entire Password Program

Here is the whole listing.  You should really try it and study it.  This is a great sample for all the concepts we have been learning.

#include <iostream>
#include <string>
using namespace std;

int main()
{
string password;
bool validated;
char letter;
int length;

// Get password candidate from console, until it passes our tests
validated = false;
while (!validated)
{
// get the password candidate from user
cout << "Enter a password, please ('q' to quit): ";
getline(cin, password);

length = password.length();
// test for quit first
if ( length == 1
&& ( password[0] == 'q'  || password[0] == 'Q') )
{
cout << "No password  defined.\n";
break;  // from loop
}

// test for reasonable length
if (length < 6 || length > 15)
{
cout << "Password must be between 6 and 15 characters.\n";
continue;
}

validated = true;   // assume innocent entering loop
// allow only letters and numbers
for (int k = 0; k < length; k++)
{
letter = password[k];   // store in local variable so we can reuse
if (letter >= 'a' && letter <= 'z')
continue; //  the for loop, we have a good lower case letter
else if (letter >= 'A' && letter <= 'Z')
continue; //  the for loop, we have a good upper case letter
else if (letter >= '0' && letter <= '9')
continue; //  the for loop, we have a good numeral
else
{
//  this letter is not one of the three legal types
cout << "Use only A-Z, a-z or 0-9, please.";
validated = false;
break;   // from the for loop leaving validated as false
}
}

// if the above loop yielded an error, we try again
if (!validated)
continue;

// if we made it here, it is the proper length and has legal chars
// but is the first character a letter?
letter = password[0];
if (letter >= '0' && letter <= '9')
{
cout << "First character must be a letter (non-numeric).\n";
validated = false;
continue;
}
else
{
// they passed the final test
cout << "Your password " + password + " has been accepted.\n";
break;   // this is not really needed but is safe
}
}

cout << "\nGood bye.\n\n";
}


/* ----------- Output ----------------

Enter a password, please ('q' to quit): hi mom
Use only A-Z, a-z or 0-9, please.
Enter a password, please ('q' to quit): sdfkj(*&
Use only A-Z, a-z or 0-9, please.
Enter a password, please ('q' to quit): 8thhing
First character must be a letter (non-numeric).
Enter a password, please ('q' to quit): sdf
Password must be between 6 and 15 characters.
Enter a password, please ('q' to quit): adfa sdfjasdlfkjasdlfkjasdf
Password must be between 6 and 15 characters.
Enter a password, please ('q' to quit): sdf sdf
Use only A-Z, a-z or 0-9, please.
Enter a password, please ('q' to quit): asdfASDF
Your password asdfASDF has been accepted.

Good bye.

------------------------------------- */

4A.6.3 A Shortcut - Character Test Functions

The #include files that you are placing on the top of your program (like <iostream>, <string> and <sstream> of past programs) provide access to many nice methods that you can use to make your life easier.  For example, from <string> you get access to the methods like length() and getline() that you have used.  There is another set of methods that are also useful, and they are available to you by virtue of your having included either <string> or <iostream> (both of which we have in this program).

One such method is the isalpha() method which tells whether or not the char that you are looking at is a letter (as compared with a digit or other symbol).  Another method is the isdigit() method, which reveals whether or not a char value is a numeral '0' through '9'.  Yet another method is isalnum() which tells whether a character is a letter or number.  There are many others (islower(), isupper(), toupper(), tolower(), etc. These methods do cool things.

Start Using Character Methods in your Homework

These methods can be used to do very useful things like converting a character to UPPER CASE or testing to see if a character is a letter. Follow the link below and experiment with some of these methods.

Check out this page for a complete list:

These methods all return either true or false so we can test them directly in an if condition.

We can use these methods to shorten the above program.  For example, we can use the following test instead of the lengthy one it replaces:

letter = password[k];
if (isalnum(letter))
continue;
else
{
// ...
}

We shorten a later test in the same program:

if (!isalpha(letter))
{
cout << "First character must be ...";

We can also save work in other programs by first converting a character to upper case before testing it (using toupper()). This way, if we want to know if the user gave us a 'D', but will accept either that or a 'd', we don't have to test against both. We convert it to uppercase first and then test only for 'D'. Here's an example that improves the above program:

// test for quit first
if ( length == 1
&& ( toupper(password[0]) == 'Q') )
{
cout << "No password  defined.\n";
break;  // from loop
}

That will just about do it for this lesson.  Good work -- you learned a lot.