Lesson 10 - Strings in C++ - Working with single characters
Lesson highlights
Are you looking for a quick reference on ASCII codes in C++ instead of a thorough-full lesson? Here it is:
Converting between characters and their ASCII value:
{CPP_CONSOLE}
char c; // character
int i; // ordinal (ASCII) value of a character
// conversion from text to ASCII value
c = 'a';
i = (int)c;
cout << "The character '" << c << "' was converted to its ASCII value of " << i << endl;
// conversion from an ASCII value to text
i = 98;
c = (char)i;
cout << "The ASCII value of " << i << " was converted to its textual value of '" << c << "'" << endl;
cin.get();
{/CPP_CONSOLE}
Would you like to learn more? A complete lesson on this topic follows.
In the previous lesson of the C++ basics course, Strings in the C++ language, we learned to
work with string
s and introduced the standard string functions. In
today's tutorial, we're going to continue with strings and introduce ASCII
values. We'll also create other useful applications.
The ASCII value
Maybe you've already heard about the ASCII table. Especially, in the MS-DOS
era when there was practically no other way to store text. Individual characters
were stored as numbers of the byte
datatype (as 1 byte which is the
char
type in C/C++), so of a range from 0
to
255
. The system provided the ASCII table which had 256
characters and each ASCII code (numerical code) was assigned to one
character.
Perhaps you understand why this method is no longer as relevant. The table
simply could not contain all the characters of all international alphabets. We
now use Unicode (UTF-8) encoding where characters are represented in a different
way. However, in C++, we tend to use ASCII values in the default configuration.
If we wanted to work with UNICODE characters (UTF-8), we'd have to use wide
characters. The main advantage of using plain ASCII is that the characters are
stored in a table next to each other, alphabetically. For example, at position
97
we'd find "a"
, at 98
"b"
,
etc. It's the same with numbers, but unfortunately, the accent characters are
messed up.
Now, let's convert a character into its ASCII value and vice-versa:
{CPP_CONSOLE}
char c; // character
int i; // ordinal (ASCII) value of a character
// conversion from text to ASCII value
c = 'a';
i = (int)c;
cout << "The character '" << c << "' was converted to its ASCII value of " << i << endl;
// conversion from an ASCII value to text
i = 98;
c = (char)i;
cout << "The ASCII value of " << i << " was converted to its textual value of '" << c << "'" << endl;
cin.get();
{/CPP_CONSOLE}
The result:
Console application
The character 'a' was converted to its ASCII value of 97
The ASCII value of 98 was converted to its textual value of 'b'
Character occurrence in a sentence analysis
Let's write a simple program that analyzes a given sentence for us. We'll
search for the number of vowels, consonants, digits and non-alphanumeric
characters (e.g. space or !
).
We'll hard-code the input string in our code, so we won't have to write it
again every time. Once the program is complete, we'll replace the string with
cin
. We'll iterate over characters using a loop. By the
way, we won't focus too much on program speed here, we'll mainly choose
practical and simple solutions.
First, let's define vowels, consonants, and digits. We don't have to count non-alphanumeric characters since they'll just be the string length minus the number of vowels and consonants. Let's set up variables for the individual counters, also. Since this will feature some relatively complex code, we'll add in comments.
// Counters initialization int vowelsCount = 0; int consonantsCount = 0; int digitsCount = 0; // the string that we want to analyze string s = "A programmer gets stuck in the shower because the instructions on the shampoo were: Lather, Wash, and Repeat."; // definition of character groups string vowels = "aeiouyAEUOUY"; string consonants = "bcdfghijklmnpqrstvwxzBCDFGHIJKLMNPQRSTVWXZ"; string digits = "0123456789"; cout << "The original message: " << s << endl; // the main loop will iterate over all the characters for (int i = 0; i < s.length(); i++) { } cin.get();
First of all, we reset the counters. For the definition of characters groups,
ordinary strings will do. The main loop iterates over each character in the
string *s*
.
Now, let's increment the counters. For simplicity's sake, I'll focus on the loop instead of rewriting the code over and over again:
// the main loop will iterate over all the characters for (int i = 0; i < s.length(); i++) { if (vowels.find(s[i]) < vowels.length()) vowelsCount++; else if (consonants.find(s[i]) < consonants.length()) consonantsCount++; else if (digits.find(s[i]) < digits.length()) digitsCount++; }
Notice that we use the find()
method which determines whether a
string contains a character. We try to find the current character from our
sentence in the vowels
string and possibly increase their counter.
If it's not included in the vowels, we look in the consonants and possibly
increase their counter. We do the same with the digits.
Now, all we're missing is the printing part at the end (displaying the text):
{CPP_CONSOLE}
// Counters initialization
int vowelsCount = 0;
int consonantsCount = 0;
int digitsCount = 0;
// the string that we want to analyze
string s = "A programmer gets stuck in the shower because the instructions on the shampoo were: Lather, Wash, and Repeat.";
// definition of character groups
string vowels = "aeiouyAEUOUY";
string consonants = "bcdfghijklmnpqrstvwxzBCDFGHIJKLMNPQRSTVWXZ";
string digits = "0123456789";
cout << "The original message: " << s << endl;
// the main loop will iterate over all the characters
for (int i = 0; i < s.length(); i++)
{
if (vowels.find(s[i]) < vowels.length())
vowelsCount++;
else if (consonants.find(s[i]) < consonants.length())
consonantsCount++;
else if (digits.find(s[i]) < digits.length())
digitsCount++;
}
cout << "Vowels: " << vowelsCount << endl;
cout << "Consonants: " << consonantsCount << endl;
cout << "Digits: " << digitsCount << endl;
cout << "Non-alphanumeric characters: " << s.length() - (vowelsCount + consonantsCount + digitsCount) << endl;
cin.get();
{/CPP_CONSOLE}
Console application
A programmer gets stuck in the shower because the instructions on the shampoo were: Lather, Wash and Repeat.
Vowels: 33
Consonants: 55
Digits: 0
Non-alphanumeric characters: 21
That's it, we're done!
The Caesar cipher
Let's create a simple program to encrypt text. If you've ever heard of the
Caesar cipher, then you already know exactly what we're going to program. The
text encryption is based on shifting characters in the alphabet by a certain
fixed number of characters. For example, if we shift the word
"hello"
by 1
character forwards, we'd get
"ifmmp"
. The user will be allowed to select the number of character
shifts.
Let's get right into it! We need variables for the original text, the
encrypted message, and the shift. Then, we need a loop iterating over each
character and printing an encrypted message. We'll also have to hard-code the
message defined in the code, so we won't have to write it over and over during
the testing phase. After we finish the program, we'll replace the contents of
the variable with a value from the cin
object. The cipher doesn't
work with accent characters, spaces, and punctuation marks. We'll just assume
the user won't enter them. To keep things simple, we'll expect the user to only
use lowercase characters. Ideally, we would remove accent characters before
encryption, as well as anything other than letters.
// variable initialization string s = "blackholesarewheregoddividedbyzero"; int shift = 1; cout << "Original message: " << s << endl; // loop iterating over characters for (int i = 0; i < s.length(); i++) { } // printing cout << "Encrypted message: " << s << endl; cin.get();
/next, we'll move into the loop. We'll increase the ASCII value of the current character by however many shifts.
{CPP_CONSOLE}
// variable initialization
string s = "blackholesarewheregoddividedbyzero";
int shift = 1;
cout << "Original message: " << s << endl;
// loop iterating over characters
for (int i = 0; i < s.length(); i++)
{
s[i] = s[i] + shift;
}
// printing
cout << "Encrypted message: " << s << endl;
cin.get();
{/CPP_CONSOLE}
Console application
Original message: blackholesarewheregoddividedbyzero
Encrypted message: cmbdlipmftbsfxifsfhpeejwjefecz{fsp
Let's try it out! The result looks pretty good. However, we can see that the
characters after "z"
overflow to ASCII values of other characters
("{"
in the picture). Therefore, the characters are no longer just
alphanumeric, but other nasty characters. Let's set our characters up as a
cyclical pattern, so the shifting could flow smoothly from "z"
to
"a"
and so on. We'll get by with a simple condition that decreases
the ASCII value by the length of the alphabet so we'd end back up at
"a"
.
// loop iterating over characters for (int i = 0; i < s.length(); i++) { s[i] = s[i] + shift; if (s[i] > 'z') // overflow control s[i] = s[i] - 26; }
If the value exceeds the ASCII value of 'z'
, we reduce it by
26
characters (the number of characters in the English alphabet).
It's simple and our program is now operational. Notice that we don't use direct
character codes anywhere. There's a 'z'
in the condition even
though we could write 122
there directly. I set it up this way so
that our program is fully encapsulated from explicit ASCII values, so it'd be
clearer on how it works. Try to code the decryption program as practice for
yourself.
In the next lesson, Solved tasks for C++ lessons 9-10, we'll introduce multidimensional arrays in C++.
In the following exercise, Solved tasks for C++ lessons 9-10, we're gonna practice our knowledge from previous lessons.
Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.
Download
By downloading the following file, you agree to the license terms
Downloaded 2x (890.52 kB)
Application includes source codes in language C++