PPP2 Chapter 11 Exercise 5

These are the specifications:

Write a program that reads strings and for each string outputs the char-
acter classification of each character, as defined by the character classifica-
tion functions presented in §11.6. Note that a character can have several
classifications (e.g., x is both a letter and an alphanumeric).


For some reason, isalpha and other such functions that work on letters don't seem to be working.

This is my code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// chapter11ex5.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 6 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 5
// Exercice Specifications:
/**
 * Write a program that reads strings and for each string outputs the character 
 * classification of each character, as defined by the character classification 
 * functions presented in §11.6. Note that a character can have several
 * classifications (e.g., x is both a letter and an alphanumeric).
 */

#include "../../cust_std_lib_facilities.h"
#include <iostream>
#include <locale>
#include <string>

int main()
{
	using namespace std;
	size_t i = 0;
	string str;
	while (getline(cin, str))
	{
		while (i < str.size())
		{
			if (isalnum(isalpha(islower(str[i])))
				&& !isdigit(str[i]))
			{
				cout << "The character '" << str[i] << "' is an alphanumeric character "
					<< "that is a lowercase letter but is not a digit.\n";
			}
			++i;
		}
	}
	cin.clear();
	cin.ignore();
	keep_window_open();
}


Just before, I'd tried to do other conditional checks for a space, a punctuation, and etc., but those conditions executed and just the ones checking for a letter didn't seem to execute. What's going on?
closed account (48T7M4Gy)
This is a start. Functions are available for sub-tests of upper and lower case etc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <string>

int main()
{
    size_t i = 0;
    std::string str;
    char ch;
    char response = 'y';
    
    while(tolower(response) == 'y')
    {
        std::cout << "Enter string: ";
        std::getline (std::cin,str);
        
        while ( i < str.length() )
        {
            ch = str[i];
            
            std::cout << "Character '" << ch << "' is a";
            if(isalpha(ch)){
                std::cout << "n alpha";
                
                if(islower(ch))
                    std::cout << " lowercase";
                else
                    std::cout << " uppercase";
            }
            else if(isnumber(ch)){
                std::cout << " number";
            }
            else if(isspace(ch)){
                std::cout << " space";
            }
            else if(ispunct(ch)){
                std::cout << " punctuation";
            }
            else
                std::cout << " ??? unknown";
            
            std::cout << " character.\n";
            
            i++;
        }
        std::cout << '\n' << "Again? <y/n> ";
        std::cin >> response;
        
        std::cin.ignore(1000, '\n');
    }
}
Enter string: What a day for Stroustrup, Ex11?
Character 'W' is an alpha uppercase character.
Character 'h' is an alpha lowercase character.
Character 'a' is an alpha lowercase character.
Character 't' is an alpha lowercase character.
Character ' ' is a space character.
Character 'a' is an alpha lowercase character.
Character ' ' is a space character.
Character 'd' is an alpha lowercase character.
Character 'a' is an alpha lowercase character.
Character 'y' is an alpha lowercase character.
Character ' ' is a space character.
Character 'f' is an alpha lowercase character.
Character 'o' is an alpha lowercase character.
Character 'r' is an alpha lowercase character.
Character ' ' is a space character.
Character 'S' is an alpha uppercase character.
Character 't' is an alpha lowercase character.
Character 'r' is an alpha lowercase character.
Character 'o' is an alpha lowercase character.
Character 'u' is an alpha lowercase character.
Character 's' is an alpha lowercase character.
Character 't' is an alpha lowercase character.
Character 'r' is an alpha lowercase character.
Character 'u' is an alpha lowercase character.
Character 'p' is an alpha lowercase character.
Character ',' is a punctuation character.
Character ' ' is a space character.
Character 'E' is an alpha uppercase character.
Character 'x' is an alpha lowercase character.
Character '1' is a number character.
Character '1' is a number character.
Character '?' is a punctuation character.

Again? <y/n>


See: http://www.cplusplus.com/reference/cctype/
Last edited on
I know about those functions from cctype, and locale also has them as shown in the chapter I'm on in the book. I just can't get the conditional statements to execute for some reason. Here's the current code;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// chapter11ex5.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 6 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 5
// Exercice Specifications:
/**
 * Write a program that reads strings and for each string outputs the character 
 * classification of each character, as defined by the character classification 
 * functions presented in §11.6. Note that a character can have several
 * classifications (e.g., x is both a letter and an alphanumeric).
 */

#include "../../cust_std_lib_facilities.h"
#include <iostream>
#include <locale>
#include <string>

int main()
{
	using namespace std;
	size_t i = 0;
	string str;
	while (getline(std::cin, str))
	{
		while (i < str.size())
		{
			if (isalnum(str[i]) && isalpha(islower(str[i])) && !isdigit(str[i]))
			{
				cout << "The character '" << str[i] << "' is a an alphanumeric character "
					"and a lowercase letter, but is not a digit\n";
			}
			else if (isalnum(str[i]) && isalpha(isupper(str[i])) && !isdigit(str[i]))
			{
				cout << "The character '" << str[i] << "' is an alphanumeric character "
					<< "and an uppercase letter, but is not a digit\n";
			}
			else if (isalnum(str[i]) && !isalpha(str[i]) && isdigit(str[i]))
			{
				cout << "The character '" << str[i] << "' is an alphanumeric character "
					<< "and a digit, but is not a letter\n";
			}
			else if (!isalnum(str[i]) && ispunct(str[i]))
			{
				cout << "The character '" << str[i] << "' is not an alphanumeric "
					<< "character but is a punctuation\n";
			}
			else if (isspace(str[i]))
			{
				cout << "The character '" << str[i] << "' is a space\n";
			}
			else if (iscntrl(str[i]))
			{
				cout << "The character '" << str[i] << "' is a control character\n";
			}
			++i;
		}
	}
	keep_window_open();
}


I'll try the code in the while you proposed as well. But yeah, why isn't it working like I want it to? Currently, it only outputs "The character ' ' is a space".
Last edited on
This looks strange:
 
    isalpha(islower(str[i]))

Why not one of
 
    isalpha(str[i])

or
 
    islower(str[i])


The return value of islower() is an integer - though it should be interpreted as a true/false value. Testing whether or not that integer is an alphabetic character doesn't make a lot of sense.
I just wanted to check in one go if the character is a lowercase or uppercase letter. So it'd really be better to separate those with the logical AND operator?

Okay, I got it to this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// chapter11ex5.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 6 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 5
// Exercice Specifications:
/**
 * Write a program that reads strings and for each string outputs the character 
 * classification of each character, as defined by the character classification 
 * functions presented in §11.6. Note that a character can have several
 * classifications (e.g., x is both a letter and an alphanumeric).
 */

#include "../../cust_std_lib_facilities.h"
#include <iostream>
#include <locale>
#include <string>

int main()
{
	using namespace std;
	size_t i = 0;
	string str;
	while (getline(std::cin, str))
	{
		while (i < str.size())
		{
			if (isalnum(str[i]) && isalpha(str[i]) && islower(str[i])
				&& !isdigit(str[i]))
			{
				cout << "The character '" << str[i] << "' is a an alphanumeric character "
					"and a lowercase letter, but is not a digit\n";
			}
			else if (isalnum(str[i]) && isalpha(str[i]) && isupper(str[i])
				&& !isdigit(str[i]))
			{
				cout << "The character '" << str[i] << "' is an alphanumeric character "
					<< "and an uppercase letter, but is not a digit\n";
			}
			else if (isalnum(str[i]) && !isalpha(str[i]) && isdigit(str[i]))
			{
				cout << "The character '" << str[i] << "' is an alphanumeric character "
					<< "and a digit, but is not a letter\n";
			}
			else if (!isalnum(str[i]) && ispunct(str[i]))
			{
				cout << "The character '" << str[i] << "' is not an alphanumeric "
					<< "character but is a punctuation\n";
			}
			else if (isspace(str[i]))
			{
				cout << "The character '" << str[i] << "' is a space\n";
			}
			else if (iscntrl(str[i]))
			{
				cout << "The character '" << str[i] << "' is a control character\n";
			}
			++i;
		}
	}
	keep_window_open();
}


It correctly identifies characters now, but it only does anything for the first stream of characters I give it. One sentence is the limit. It doesn't do anything to any other input I give it aside from EOF (at which point the loop ends).
Last edited on
closed account (48T7M4Gy)
why isn't it working like I want it to?


@OP I extended and changed my post to show you how the nested if's work for upper and lower case alpha characters.

The reason why yours are not apparently working is because your if statements are too complex for ease of checking and are probably incorrect. What's the point of verifying by a test that an alpha character is not a number other than Stroustrup thinks it's important? You don't need a computer to tell you an alpha character, whether it is upper or lower case is also not a digit/number. That level of redundancy appears to me to be silly.

But if you have your heart set on it then I think it is easier and clearer to start with the higher classification and break it down into sub-classifications separately as I show with upper and lower case. Up to you though. :)


Using the classic "C" locale:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <vector>
#include <locale>

using classic_fn = int(&)(int) ; // reference to function int(int)

std::size_t count( const std::string& str, classic_fn fn )
{
    std::size_t cnt = 0 ;
    for( unsigned char c : str ) if( fn(c) ) ++cnt ;
    return cnt ;
}

int main()
{
    std::string str ;
    while(std::getline(std::cin, str ) )
    {
        std::cout << " space: " << count( str, classic_fn(std::isspace) ) << '\n'
                  << " print: " << count( str, classic_fn(std::isprint) ) << '\n'
                  << " cntrl: " << count( str, classic_fn(std::iscntrl) ) << '\n'
                  << " upper: " << count( str, classic_fn(std::isupper) ) << '\n'
                  << " lower: " << count( str, classic_fn(std::islower) ) << '\n'
                  << " alpha: " << count( str, classic_fn(std::isalpha) ) << '\n'
                  << " digit: " << count( str, classic_fn(std::isdigit) ) << '\n'
                  << " punct: " << count( str, classic_fn(std::ispunct) ) << '\n'
                  << "xdigit: " << count( str, classic_fn(std::isxdigit) ) << '\n'
                  << " blank: " << count( str, classic_fn(std::isblank) ) << '\n'
                  << " alnum: " << count( str, classic_fn(std::isalnum) ) << '\n'
                  << " graph: " << count( str, classic_fn(std::isgraph) ) << '\n' ;
    }
}

http://coliru.stacked-crooked.com/a/78de2ff365cf3d21


Using the locale associated with std::cin:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <vector>
#include <locale>

using ctype = std::ctype<char> ;
using mask_type = ctype::mask ;

std::size_t count( const std::vector<mask_type>& masks, mask_type value )
{
    std::size_t cnt = 0 ;
    for( auto mask : masks ) if( (mask&value) == value ) ++cnt ;
    return cnt ;
}

int main()
{
    const auto& ctype_facet = std::use_facet<ctype>( std::cin.getloc() ) ;

    std::string str ;
    while( std::getline( std::cin, str ) )
    {
        std::vector<mask_type> masks( str.size() ) ;
        ctype_facet.is( std::addressof( str.front() ), std::addressof( str.front() ) + str.size(),
                        std::addressof( masks.front() ) ) ;

        std::cout << " space: " << count( masks, ctype::space ) << '\n'
                  << " print: " << count( masks, ctype::print ) << '\n'
                  << " cntrl: " << count( masks, ctype::cntrl ) << '\n'
                  << " upper: " << count( masks, ctype::upper ) << '\n'
                  << " lower: " << count( masks, ctype::lower ) << '\n'
                  << " alpha: " << count( masks, ctype::alpha ) << '\n'
                  << " digit: " << count( masks, ctype::digit ) << '\n'
                  << " punct: " << count( masks, ctype::punct ) << '\n'
                  << "xdigit: " << count( masks, ctype::xdigit ) << '\n'
                  << " blank: " << count( masks, ctype::blank ) << '\n'
                  << " alnum: " << count( masks, ctype::alnum ) << '\n'
                  << " graph: " << count( masks, ctype::graph ) << '\n' ;
    }
}

http://coliru.stacked-crooked.com/a/169b966d7dc61511
It works now, but I don't like declaring variables earlier than needed (probably good here either way, since declaring them inside the loop might take up more memory). I did it like Kemort's post (thanks, Kemort):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// chapter11ex5.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 6 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 5
// Exercice Specifications:
/**
 * Write a program that reads strings and for each string outputs the character 
 * classification of each character, as defined by the character classification 
 * functions presented in §11.6. Note that a character can have several
 * classifications (e.g., x is both a letter and an alphanumeric).
 */

#include "../../cust_std_lib_facilities.h"
#include <iostream>
#include <locale>
#include <string>

int main()
{
	using namespace std;
	size_t i = 0;
	string str;
	char ch;
	char response = 'y';

	while (tolower(response))
	{
		cout << "Enter a string: ";
		getline(cin, str);

		while (i < str.length())
		{
			ch = str[i];
			cout << "Character '" << ch << "' is a";
			if (isalpha(ch))
			{
				cout << "n alpha";
				if (islower(ch))
				{
					cout << " lowercase";
				}
				else
				{
					cout << " uppercase";
				}
			}
			else if (isdigit(ch))
			{
				cout << " number";
			}
			else if (isspace(ch))
			{
				cout << " space";
			}
			else if (ispunct(ch))
			{
				cout << " punctuation";
			}
			else
			{
				cout << " ??? unknown";
			}
			cout << " character\n";
			++i;
		}
		cout << "Again? (y/n) ";
		cin >> response;
		if (response == 'n')
		{
			break;
		}
		cin.ignore(1000, '\n');
	}
	keep_window_open();
}
1
2
3
/* ... Note that a character can have several
 * classifications (e.g., x is both a letter and an alphanumeric).
 */


Also note that 'a' is all of alpha (alphabetic), alnum (alphanumeric), xdigit (hexadecimal digit), and print (printable).
So what do I do for that? Check in the same if-condition if it's alnum and alpha, or alnum, alpha and xdigit? That might make it overly complicated and give me the same problem I had earlier, right?

Edit: It kind of works, but if I type 'y' when it asks "Again?", and enter something, it just asks "Again?" again.

Here's the code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// chapter11ex5.cpp : Defines the entry point for the console application.
// Osman Zakir
// 3 / 6 / 2017
// Bjarne Stroustrup: Programming: Principles and Practice Using C++ 2nd Edition
// Chapter 11 Exercise 5
// Exercice Specifications:
/**
 * Write a program that reads strings and for each string outputs the character 
 * classification of each character, as defined by the character classification 
 * functions presented in §11.6. Note that a character can have several
 * classifications (e.g., x is both a letter and an alphanumeric).
 */

#include "../../cust_std_lib_facilities.h"
#include <iostream>
#include <locale>
#include <string>

int main()
{
	using namespace std;
	size_t i = 0;
	string str;
	char ch;
	char response = 'y';

	while (tolower(response))
	{
		cout << "Enter a string: ";
		getline(cin, str);

		while (i < str.length())
		{
			ch = str[i];
			cout << "Character '" << ch << "' is a";
			if (isalnum(ch) && isalpha(ch) && isxdigit(ch) && isprint(ch))
			{
				cout << "n alpha, alphanumeric, hexadecimal digit, and printable";
				if (islower(ch))
				{
					cout << " lowercase";
				}
				else
				{
					cout << " uppercase";
				}
			}
			else if (isalnum(ch) && isalpha(ch) && isprint(ch) && !isxdigit(ch))
			{
				cout << "n alpha, alphanumeric, and printable, but not a hexadecimal";
			}
			else if (isalnum(ch) && isdigit(ch) && isprint(ch))
			{
				cout << " number, alphanumeric, and printable";
			}
			else if (isalnum(ch) && isdigit(ch) && isxdigit(ch))
			{
				cout << " an alphanumeric, and hexadecimal digit";
			}
			else if (isalnum(ch) && isxdigit(ch))
			{
				cout << "an alphanumeric and hexadecimal number";
			}
			else if (isspace(ch))
			{
				cout << " space";
			}
			else if (ispunct(ch) && isprint(ch))
			{
				cout << " punctuation and a printable";
			}
			else
			{
				cout << " ??? unknown";
			}
			cout << " character\n";
			++i;
		}
		cout << "Again? (y/n) ";
		cin >> response;
		if (response == 'n')
		{
			break;
		}
		cin.ignore(1000, '\n');
	}
	keep_window_open();
}
Last edited on
closed account (48T7M4Gy)
You reach a point with this exercise that it becomes over-egged but the following demonstrates how to solve a couple of problems raised. I think it is clearer to cascade simple if's rather than complex and/or and not combinations. Either that or go for JLBorges solution which just hammers out the result.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <string>

int main()
{
    size_t i = 0;
    std::string str;
    char ch;
    
    char response = 'y';
    bool keep_going = true;
    
    while(keep_going)
    {
        i = 0;
        
        std::cout << "Enter string: ";
        std::getline (std::cin, str);
        
        while ( i < str.length() )
        {
            ch = str[i];
            
            std::cout << "Character '" << ch << "' is ";
            if (isprint(ch) )
            {
                std::cout << "printable ";
                
                if(isalnum(ch))
                {
                    std::cout << "alpahanumeric ";
                    if(isalpha(ch))
                        std::cout << "alphabetic ";
                    else
                        std::cout << "non-alphabetic ";
                }
                
                else
                    std::cout << "non-alphanumeric ";
            }
            else
                std::cout << "non-printable ";
            
            std::cout << '\n';
            
            i++;
        }
        
        std::cout << '\n' << "Again? <y/n> ";
        while(std::cin >> response and ( tolower(response) != 'y' and tolower(response) != 'n') )
        {
            
            std::cout << "y or n only\n";
        }
        
        if(response == 'y')
            keep_going = true;
        else
            keep_going = false;
        
        std::cin.ignore(1000, '\n');
    }
}


Enter string: what 124$
Character 'w' is printable alpahanumeric alphabetic 
Character 'h' is printable alpahanumeric alphabetic 
Character 'a' is printable alpahanumeric alphabetic 
Character 't' is printable alpahanumeric alphabetic 
Character ' ' is printable non-alphanumeric 
Character '1' is printable alpahanumeric non-alphabetic 
Character '2' is printable alpahanumeric non-alphabetic 
Character '4' is printable alpahanumeric non-alphabetic 
Character '$' is printable non-alphanumeric 

Again? <y/n> b
y or n only
k
y or n only
n
Program ended with exit code: 0
Another option:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <string>
#include "std_lib_facilities.h"

void reportChar(char ch)
{
  if (ch < 0) // avoids crash if ch is negative
    return;

  cout << "Character '" << ch << "' is ";
  if (isspace(ch))
    cout << " isspace";
  if (isalnum(ch))
    cout << " isalnum";
  if (isprint(ch))
    cout << " isprint";
  if (isalpha(ch))
    cout << " isalpha";
  if (isdigit(ch))
    cout << " isdigit";
  if (isxdigit(ch))
    cout << " hex-digit";
  if (isupper(ch))
    cout << " isupper";
  if (islower(ch))
    cout << " islower";
  if (iscntrl(ch))
    cout << " control";
  if (ispunct(ch))
    cout << " puntuation";
  if (isgraph(ch))
    cout << " isgraph";
}

bool confirm(const string& msg)
{
  cout << msg << " ";
  char ch = cin.get();
  cin.ignore(255, '\n');

  return toupper(ch) == 'Y';
}

int main()
{
  std::string str;
  
  for (;;)
  {
    cout << "Enter string: ";
    getline(cin, str);
    for (char ch : str)
    {
      reportChar(ch);
      cout << "\n";
    }
    if (!confirm("Again (y/n)"))
      break;
  }
  
  keep_window_open();
  return 0;
}
Thomas' one is perfect for me. Thanks, man.
Topic archived. No new replies allowed.