Chapter 9 Strings

advertisement
Chapter 9
Strings
0. Introduction
C-strings are the kind of strings C++ gets from its C language heritage. A C-string is an
ordinary array of char with the additional termination requirement. The last character
position used in the array must be indicated by a null character (‘\0’) in the next
position.
The Standard C++ library provides the class string. This class is defined in the
header <string>. This class has many useful member functions and overloaded
operators that make string processing easier and more intuitive. This class is not properly
part of the Standard Template Library. Nevertheless, the development of the STL
influenced the design of the string class as it appears in the ANSI/ISO C++ Standard
and in implementations provided by compiler developers.
We will call the type of string C++ inherits from C, C-strings. We will call a C++
Standard string class object simply a “string”.
1. Outline of topics in the chapter
9.1 An Array Type for Strings
C-string values and C-string variables
Other Functions in <cstring>
C-string input and output
9.2 Character Manipulation
Character I/O
The Member Functions get and put
The putback, peek, and ignore Member Functions
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 2
Character-Manipulating Functions
9.3 The Standard class string
Introduction to the Standard class string
I/O with the class string
string processing with class string
Converting Between string objects and C-strings
2. General remarks on the chapter
9.1 An Array Type for Strings
C-string values and C-string variables
A type is a collection of values and allowable operations. The values that cstrings can
have are things like "Hi, Mom!". These are also called cstring literals. We know about
cstring operations, (copying, concatenation, to mention two) but we don't presently
have any way to implement these in an easily used fashion, and don't have many details
of cstrings. This section of the text provides these details.
Before launching into the text's discussion of cstrings and arrays, some of the details
of C and C++ "escape sequences" or special characters will be presented. The text's
discussion of cstring variables points out that the null character '\0' is the terminator
for cstring variables. The text points out that the library functions that process
cstrings (including the iostream functions) use the null character as a sentinel
indicating the last character of the cstring. The backslash (\) signals that the next
character to be dealt with in a way different than it would be handled without the
backslash. The backslash is called the 'escape character' because it escapes the normal
meaning of certain characters, allowing them to have special meaning. (And it removes or
escapes special meaning of characters that have special meaning, such as the backslash
itself. To print a backslash, use two backslashes \\ in the cstring literal. Here the
first backslash escapes the special meaning of the second backslash, which is inserted in
the output stream.)
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 3
If we just put a 0 into some position in a cstring, the encoding for 0, 48 decimal, or
30 hexadecimal, is stored at that character position. (See Appendix 3 for the decimal
encoding of the printing ASCII characters.) To cause the null character to be stored, we
use the \ before the 0 to escape the usual meaning of 0. This tells the compiler that we
want the numeric value of the null character to be embedded in the cstring. (The
value of the null character really is zero, not the normal encoding of the character 0.)
C++ provides a number of the 'escape characters'. we have already see \t, the tab
character. It was used to help format output programs from previous chapters. The text
discusses \0 and \n, the null and the new line characters. I include them all here
because some of my students have had problems understanding what happened to certain
characters in their output. If you, the instructor, are aware of these, you are in a better
position to help the student.
null
newline
horizontal tab
vertical tab
backspace
carriage return
digit
formfeed
\0
\n
\t
\v
\b
\r
alert or bell
backslash
question mark
single quote
double quote
octal number
\a
\\
\?
\'
\"
\ooo
\f
Hex number
\xhhh h = hex digit
o = octal
If you use a backslash in front of a character not mentioned here, the compiler should
issue a warning. Borland’s BCC 5.5 does not, whereas g++ and VC++6.0 do issue
warnings about an unknown or unrecognized escape sequence.
In connection with the remark in the text on page 353 that the following declarations are
not equivalent:
char shortStringA[] = {'a', 'b', 'c'};
and
char shortStringB[] = "abc";
A quick way (under Linux, UNIX TM and other UNIX-like operating systems) to
convince the student that these are not equivalent is to put these declarations in a program
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 4
and send them to the screen. Using g++ under Linux, when I send endl to the output
stream after each of these, I get: "abc" from the first output statement, then "abc"
followed by several ugly characters from memory locations following the array, up to the
next ‘\0’ (or the next memory location that is protected, when I get a segmentations
violation).
Running the program then piping the result to od, the Unix octal dump program, with
the byte by byte output command line switch (od -b), I get
17:28:35:~/AW$ a.out | od -b
0000000 141 142 143 277 320 374 377 277 071 012 141 142 143 012
0000016
17:28:42:~/AW$
The 0000000 is the number (offset from the start) of the first byte, the 141, 142,
and 143 are the octal encoding for 'a', 'b', and 'c'. Following these are
numbers there are 7 octal numbers that are not ASCII characters. They have the 'high bit
set' and generate the PC's extended characters (that I cannot reproduce here). There is a
second sequence of 141, 142, and 143. These are the output from the second
cstring. These are octal representations for 'a', 'b', and 'c'. The
00000016 is the octal number for decimal 14. This is the number of a byte that is
one past the last number listed on the previous line.
Other Functions in <cstring>
The library functions declared in <cstring> are unlike the template libraries such as
<vector>. The definitions are templates and are mostly in the header file. The
cstring library is precompiled so it has its declarations mostly in the header and the
definitions mostly precompiled and placed in libraries to which your program is linked
after compiling.
There is a host of cstring functions declared in the header <cstring> header file. In PJ
Plauger's Standard C Library, I count 22 such functions in the string.h. All of
functionality declared in <string.h> appears in the C++ <string> header file. Most
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 5
of these functions are variations on the <cstring> functions listed in the text, or their
equivalents that move a chunk of raw memory. With just a little care, almost anything
you need to do will be easily done with just the functions in the text.
Any use of these functions requires considerable care to ensure the preconditions for
these functions are met. (See the next section in the text "Defining Cstring Functions".)
The most critical issue is that there must be space enough in the destination to hold the
result.
Typographical Error in Display 9.1
In the first printing there are a couple of typographical errors in Display 9.l. The three
argument C-string functions all have names with the letter n in the middle. For
example, strcpy(target, source, limit) should be
strncpy(target, source, limit). The other names should be strnncat
and strncmp. These changes will be made in the next printing.
PITFALL: Dangers in Using functions defined in <cstring>
A danger in using functions from <cstring> lies in using arrays of char without a null
terminator as the preconditions prescribe. Each of the functions strcpy, strcat and strcmp
depend on the terminating null character for their action. In addition, the iostream output
function expects there to be a terminating null character. Otherwise, as we note elsewhere
in this IRM, the output routine will run on until either we encounter a null character or
protected memory.
Here is an additional bit of warning about the strcmp function. I want to expand on the
text's discussion of the strcmp function.
The strcmp function is required only to return a positive number (not necessarily +1) if
the second string argument is cstring argument is lexicographically greater (later in
dictionary ordering) than the first cstring argument, and a negative number (not
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 6
necessarily –1) if the second cstring argument is lexicographically earlier than the first
cstring argument. For example,
x = strcmp( "aaron", "aardvark" );
assigns a positive number to x. This is not necessarily 1, though some implementation
may indeed return 1 in this circumstance. If I were implementing strcmp, I would
return the difference of the ASCII value of the first characters that differ in the two
strings. If all the characters were the same, I would return 0.
In fact, strcmp from Borland’s library returns the difference of the ASCII encoding
values for the first characters that are different, whereas gnu and VC++ <cstring>
versions of strcmp return 1 or -1 when the strings are different.
Rule: Do not write code that depends on +1 or -1 being returned from strcmp!
Pitfall toupper and tolower return int values (Page 374 of the text.) suggests this
extension of the above rule:
Extended Rule: Prior to using any library routine, carefully examines the
declaration in the header, and examine the pre- and post-conditions in your library
reference before you use a library function.
C-string input and output
Suppose non-digit input is received by the first loop in the following:
#include <iostream>
using namespace std;
int main()
{
int x;
cout << "enter some int values: \n";
while( cin >> x ) //intVariable )
{
cout << "You pressed " << x << "\n";
}
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 7
cout << "finished" << endl;
cout << "enter some more int values: \n";
while( cin >> x ) //intVariable )
{
cout << "You pressed " << x << "\n";
}
cout << "finished" << endl;
return 0;
}
The first loop terminates when non-digit input is encountered. The input stream
is set to stream state of bad, and any later input attempt fails. By convention, all
input functionality is disabled with the stream state is set to other than a good
state.
We sometimes need to be able to restore the i/o state to good. To do this, we
must reset the i/o state to goodbit and remove the offending characters. The
iostream library has two member functions to change the stream state explicitly.
Here the prototype of the first one.
void clear(iostate state = goodbit);
This function sets the state of the stream to the argument, where the function has
a default argument of goodbit. Thus if you issue the call
istreamObject.clear();
then the state flags are set to goodbit.
A second function has prototype
void setstate(iostate addstate);
This function sets the state to whatever the state of the stream was prior to the
call, (let’s calll this oldStreamState) to
oldStreamState | addstate
In this expression, the | is the bitwise OR operator.
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Example:
You can reset the state of the input stream by…
#include <iostream>
using namespace std;
int main()
{
int x;
cout << "enter some int values: \n";
while( cin >> x ) //intVariable )
cout << "You pressed " << x << "\n";
cout << "finished" << endl;
if(cin.bad())
{
cout << "unrecoverable error\n";
exit(1);
}
else if(cin.fail())
{
cout << "cin.fail() returned true.\n";
char discard;
cin.clear();
// We must clear the stream flags first.
cin >> discard; // The offending input is a char. It
// remains on input queue and must be
// removed and discarded before
// continuing with input operations
cout << "discarded bad char:
" << discard << endl;
}
cout << "enter some more int values: \n";
while( cin >> x ) //intVariable )
cout << "You pressed " << x << "\n";
cout << "finished" << endl;
Page 8
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 9
return 0;
}
A last note on this section: Please be certain the student is aware of the necessity
for providing space for the null terminator when using getline as well as in
other C++ string uses. A typical call might look like this
.
cin.getline(stringVariable, MAX_CHARACTERS + 1);
The getline member function overwrites the first argument (let's call it arg1)
with the smaller of strlen(arg1) characters (including the null terminator)
and MAX_CHARACTERS. A null character is always appended after the
MAX_CHARACTERS number of characters. This caution is expressed in the text
on page 362 in the getline sidebar. It bears emphasis.
9.2 Character Manipulation
Character I/O and The Member Functions get and put
The following code is a filter1 illustrating the clean, simple code that the iostream library
provides. I illustrate some of the methods used with the iostream identifiers to control
looping. This program will duplicate any file under UNIX. (Under other operating
systems, this may not be the case. In particular, MS Windows uses a strongly typed file
system. I don’t know much about the internals of any version of MS Windows, so I
cannot guarantee that it will copy files there. It appears to do so.)
// file: size.cc
// Purpose: file-copying filter
// usage: prog < infile > outfile
#include <iostream>
using namespace std;
int main()
{
while (cin)
1
A filter is a stand-alone function that reads the standard input does some task to the information that it has
read, then writes the standard output. In this case, we have the simplest possible filter, a filer that copies its
input to the output.
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 10
cout.put(cin.get());
}
It is interesting to note that cin.get() returns an int. The put member takes a
char argument which causes the int to be converted to a char. If the loop in this
example is rewritten:
while (cin)
cout << cin.get();
then the output is a sequence of int type ASCII codes for the input characters. A cast
to char would be required to get the same behavior:
This points up the need to know the return type for each member function. Here are full
declarations (prototypes) for the get members of istream and the put member of
ostream:
ostream& ostream::put(char source);
and
int istream::get();
istream& istream::get(char& destination);
I am using the first of the get functions, the text uses only the second. The text's usage
ignores the istream reference return value, since this value is either ignored, in the first
line of code below, or automatically used as in the second line of code following.
operator << “This line ignores operator
<< return value.\n”;
cout << “The value of x is “ << x ;
The precedence of the << is left-to-right. The execution proceeds:
(cout << “The value of x is “) << x ;
The code inside the parentheses is executed, returning a reference to the ostream. This is
used to carry out the outer << , which again returns an iostream reference, which is
ignored. This is not unlike traditional C where the return value for printf is usually
ignored:
printf(“The value of x is %d “, x );
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 11
Note that there are several other overloadings for get in istream and for put in
ostream which do other important things that will be dealt with later. Care and reading
the manual are necessary here.
The putback, peek, and ignore Member Functions
The putback function returns the argument character to the input stream then
decrements the next-character pointer. The declaration of the putback member
istream function is
istream& putback(char c);
The precondition for this function is that the char argument be the last character
extracted, or if the istream state is not good, this function calls
setstate(failbit) which may throw the exception, ios_base::failure, and
return. If we ignore exceptions, the program will just terminate. We won’t discuss
exceptions until Chapter 18. The instructor needs to be aware of this because inevitably
some student will run into a situation where her program terminates inexplicably.
The peek function allows us to examine a not-yet-read char from the input.
This istream function has declaration
int peek();
The function peek returns an EOF if at end of file, or if the function ios::good()
returns false. Being able to examine a character prior to reading it is desirable,
particularly when we want robust input that allows for human error at the keyboard.
The ignore function extracts and discards a number of characters equal to the first
argument or characters up to a character equal to the second argument. The declaration
for this istream member is
istream& ignore(streamsize n = 1, int delim = EOF);
The type streamsize is an unsigned integral type. In most implementations, this is
unsigned int.
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 12
Character-Manipulating Functions
These functions are of two kinds, case conversion and classification functions. All these
functions have an int parameter and return an int value.2
The declarations of the conversion functions are:
int toupper(int);
int tolower(int)
The C++ library functions accept an int that is the ASCII representation for a character.
If this character is not a lowercase letter, the return value is the int value of the
representation of the character. If this character is a lowercase letter, the return value is
the int value of the representation for the corresponding upper case letter.
Implementations I have access to test for lowercase range then return the sum or
difference of (‘A’ - ‘a’) and the parameter. If the parameter is not a lower case
letter, the parameter is returned unchanged.
In short, these functions take int parameters and return an int. Warn the student that
blind use of these functions result in int output instead of char output.
Example:
char x;
while(cin.get(x))
cout << toupper(x);
This will generate numbers in the output instead of uppercase letters.
The other functions have int parameters and return an int value, not as might be
expected, a Boolean value. The value returned by a successful test is only guaranteed to
This is an artifact of C++’s inheriting many of its libraries from C, including the library functions
described in this section. Late addition of the Boolean type bool was also an impact.
2
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 13
be nonzero. The returned value is specifically not guaranteed to be 1.
Warn the student that code should not depend on the specific value returned by these
functions.
These functions classify depending on whether the argument is the encoding of a have
one of several characteristics:

uppercase

lower case,

digit,

letter or digit,

white space,

punctuation, and

printing character (not a control character or delete)
9.3 The Standard class string
Introduction to the Standard class string
The C++ string class was added, substantially in its present form, to the C++ (draft)
Standard library circa 1994.
For the Instructor: The class basic_string is a template that uses either template
parameter char or wchar_t (wide characters) for the template parameter. This enables
wide characters for support of international character sets (i.e., Unicode). In most C++
implementations I have seen, the name string is a typedef of the instantiation
basic_string<char>. Rewriting string as a template was a major change from
the version of string adopted in 1994.
The string class overloads many operators: + and += for concatenation, = for
assignment, <, <=, >, and >=, for string comparison use lexicographic ordering, and
the indexing operation, [i], for access to characters in the string. There is no range
checking for indexing on string objects. If range checking is needed, there is a string
member function called at() that is range checked. It provides many member functions
that provide the following useful functionality. See page 387, Display 9.7 for typical uses
of functions providing the following functionality.

several versions of find

substring search

search for any character from an argument string in the calling string object.

search for any character not from an argument string in the calling string object.
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 14

extract a substring from the calling string object at a starting position with specified
length.

insert in the calling object an argument string at a specified position.

remove a substring from the calling object starting at a specified position having
specified length.
I/O with the class string
The insertion, <<,and extraction, >>, operators are overloaded for string objects. By
default, extraction to a string object
string s;
cin >> s;
ignores initial whitespace, just at extraction to a C-string. This operation then extracts the
next sequence of nonwhite characters, and stops input at the next white space.
There is a version of getline defined specifically for string objects. The syntax is
string s;
getline(cin, s);
This version of getline is necessarily a stand-alone function. The committee of
authors of the string library had two choices. They could have rewritten the
iostream library to add a getline member function, or they could have written a
stand-alone getline function. The iostream library was already written and stable.
Just as you have to write a stand-alone overloading of the output operator for your
classes, these authors chose to write a stand-alone overloaded version of getline for
this pair of arguments.
The getline function extracts from the input stream characters up to the delimiter
character. The characters from the start to the delimiter are placed in the string variable,
and the termination character is discarded. The delimiter character defaults to the newline
character, ‘\n’. By supplying a third argument, the default termination character can be
changed. It is a bad practice to mix cin >> x style input and getline.
Note, however that pre-patchlevel 5 VC++ compilers 6.0 produces anomalous behavior
for this code fragment when run from the command line.
string line;
for(int i = 0; .i < 5;.i++)
{
getline(cin, line);
cout << line << endl;
}
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 15
It is always a good idea to try this for yourself. However, it is essential that you try these
ideas if you are using VC++ before taking these ideas to the classroom or assigning
problems involving these ideas.
string processing with class string
With the exception of the palindrome code, I have talked about everything in this section
o f the text earlier.
Converting Between string objects and C-strings
Several functions require C-string values as arguments. This section shows how to use
the cstr() member to retrieve a null terminated C-string r-value from a string
object.
3. Solutions to, and remarks on, selected Programming Projects
1. Format a Sentence.
While it may be cleaner to use C++ Standard class string to do this problem, I chose to
use only the features of <cstring> to do this problem. Students need to be able to use
both <string> and <cstring> facilities.
This program is to read in a (single) sentence (defined as a sequence of words terminated
by a period (.). The sentence is to be formatted and written to the output. Formatting
consists of capitalizing first words in a sentence and compressing all multiple blanks to
single blanks. For this purpose, a line-break is a blank. No other capital letters are to be
included in the output. A sentence is assumed end with a period and there are no other
periods.
Input:
Get a 100 character line. The period is the terminating sentinel.
Process: scan the line, capitalizing the first letter in the string, looking for multiple blanks
which it eats, except for one, and newline == blank.
Some questions asked during design:
Do I replace new-lines with blanks?
Answer: Defer this question until later. (Answered later: yes)
What do I do with sentences that are longer than my line width on my output device?
Answer: Scan for and replace new-lines with blanks before replacing multiple blanks
with one blank, and allow wraps on output.
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 16
Output:
Copy the modified string to the output.
This solution uses only the C style string (C-string) features.
The input is taken character by character. If a period (.) is encountered, the loop
terminates.
Process:
First, any new-line characters are replaced by blanks.
Next, all characters are made lower case.
There is a one-time switch to capitalize the first non-blank.
There is a state machine for compressing multiple blanks to single blanks:
If we have a blank and haven't seen one since the last non-blank, set a variable,
haveBlank. Next if we see a blank and haveBlank is already set, cause overwriting
the current blank with the new one by backing up the index to the sentence array by one.
Once we read a non-blank, if the haveBlank note is set to true, turn it off.
These pieces, along with replacing the newlines with blanks, serve to compress each
sequence of blanks and newlines to one blank.
Finally, there is the matter of putting in a null character if the string is shorter than the
limit, and putting in a period and null if the string is too long.
Note:
An alternative solution is to use a loop around cin >> word, where word is a Cstring variable. This ignores blanks and newlines. Then process the words much as I have
processed the characters here. Then output the words with a single blank between.
#include <iostream>
#include <cstring>
#include <cctype>
void getSentence(char sentence[], int& size);
// fetches characters for sentence until a period. The period is
// put at the end of characters fetched. size is set to number of
// characters fetched, including the period.
void process(char str[], int size);
// replaces newline by blanks, compresses multiple blanks to one
// capitalizes the first letter of the first word, lower cases rest.
int main()
{
using namespace std;
char sentence[100];
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 17
int size;
cout << "Enter one sentence. The period is the sentinel to quit."
<< endl;
getSentence(sentence, size);
cout << endl;
cout << sentence << endl;
}
void getSentence(char sentence[], int& size)
{
int haveBlank = false;
int isFirstLetter = true;
for(int i = 0; '.' != (sentence[i] = cin.get()) && i < 100; i++)
{
if('\n' == sentence[i])
sentence[i] = ' ';
sentence[i] = tolower(sentence[i]);
if(isFirstLetter && isalpha(sentence[i]))
{
isFirstLetter = false;
sentence[i] = toupper(sentence[i]);
}
if(' ' == sentence[i] && !haveBlank)
haveBlank = true;
else if(' ' == sentence[i] && haveBlank)
i--;
else if(' ' != sentence[i] && haveBlank)
haveBlank = false;
}
if(i < 99)
sentence[i+1] = '\0';
else
{
sentence[98] = '.';
sentence[99] = '\0';
}
size = i;
}
A typical run follows:
Input:
noW
iS
thE
TiMe fOr
gOOD MEN TO ComE TO
aLl
tHe
aId
oF
ThE
CounTRY.
Output:
Enter one sentence. The period is the sentinel to quit.
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 18
Now is the time for all good men to come to the aid of the country.
2. Count words and letters
This program reads in a line of text, counts and outputs the number of words in the line
and the number of occurrences of each letter.
Define a word to be a string of letters delimited by white space (blank, newline, tab), a
comma, or a period. Assume that the input consists only of characters and these
delimiters.
For purposes of counting letters, case is immaterial.
Output letters in alphabetic order and only output those letters that occur.
Algorithm development:
The word count is carried out with a state machine.
We enter with our state variable, inWord set to false, and our word count set to 0.
while(input characters is successful)
if we have encountered a blank, newline or a tab,
we set state to false
else if inWord is false,
set state to true
increment word count
lowCase = tolower(inChar);
charCount[int(lowCase) - int('a')]++;
cout << wordCount << " " words" << endl;
for(i = 0; i < 25; i++)
if(charCount[i] != 0)
cout << charCount[i] << " " << char(i + 'a')<< endl;
Comments on the letter count code:
We run tolower() on all characters entered. The data structure for the letter count is a
26-letter int array with indices in the range 0-25, calculated by
index = int(character) - int('a')
as each letter is read in, increment the appropriate array element.
Output of the letter count is a loop running from 0-25, with an if statement that allows
output if the array entry isn't zero.
The word counting results of this agree with the Unix wc utility, and agree with hand
counted simple files. I do not supply test results.
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 19
#include <iostream>
#include <cctype>
// character and word count.
int main()
{
using namespace std;
int inWord = false;
int wordCount = 0;
char ch;
char lowCase;
int charCount[26];
int i; //i is declared outside for to avoid warnings about
//differences between new and old binding rules for the
//for loop variable.
for(i = 0; i < 26; i++)
charCount[i] = 0;
while('\n' !=(ch = cin.get()))
{
if(' ' == ch || '\n' == ch || '\t' == ch)
inWord = false;
else if(inWord == false)
{
inWord = true;
wordCount++;
}
lowCase = tolower(ch);
charCount[int(lowCase) - int('a')]++;
}
cout << wordCount << " words" << endl;
for(i = 0; i < 25; i++)
if(charCount[i] != 0)
cout << charCount[i] << " " << char(i + 'a')
<< endl;
return 0;
}
3 First, Middle and Last Names
Only notes are provided for this problem.
Problem Statement:
This program is supposed to accept first name, middle name or initial, then last name.
Even if the middle name is only an initial, an initial followed by a comma, the output
should be
lastName, firstName, middleInitial. (period after middle initial)
If the middle name is missing altogether the output should be
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 20
lastName, firstName
Notes:
This is easy if there is at least a middle initial. Using three variables,
char firstName[20], middleName[20], lastName[20];
This works well for most names. The input is:
cin >> firstName >> middleName >> lastName;
and for output:
cout << lastName << ", " << firstName
<< middleName[0] << '.' << endl;
If the middle name is missing, the program must detect this (strlen), copy the
middle name to the last name, and set the middle name to an empty string (or just arrange
not to print the middle name).
Or you can use <string> functions.
4 Text replacement
This program accepts a line of text. It replaces all four-letter words in the string with
"love". If the four-letter word is capitalized, the word love should be also. Only capitalize
the first letter of the word.
If the length is 4, call output(char firstLetter) that outputs Love or love
depending on whether the firstLetter is uppercase or lower case.
A Simple Minded “Almost Solution”:
In developing this, I came upon a very simple minded solution that almost meets the
requirements of the problem. I present that first.
Caveat: This solution collapses all blanks to one blank, and requires that one must enter
the end-of-file character to terminate the input, rather than "accepting a line of text."
But it is a simple solution:
#include <iostream>
#include <cctype>
void outputLove( char firstLetter );
// word has 4 letters, if firstLetter is uppercase, output Love
// else output love
int main()
{
using namespace std;
char word[81];
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 21
while ( cin >> word )
{
if ( strlen(word) == 4 )
outputLove( word[0] );
else
cout << word;
cout << " ";
}
cout << endl;
}
void outputLove( char firstLetter )
{
using namespace std;
if ( isupper( firstLetter ) )
cout << "Love" ;
else
cout << "love";
}
A “Better” Solution
(Is it better? - It is certainly a more complicated solution. It does meet the requirements of
the problem exactly. However, I am fond of the simpler solution. )
The following solution accepts a line of input. It uses cin member getline to fetch a
line of input. A function, breakUp, separates the non-alphabetic and alphabetic
substrings of the input line into an array of strings. The main function then examines the
strings one at a time and if alphabetic and of length 4, prints 'love' or 'Love'
depending on case of the string being replaced. The main part prints the string intact if it
is of length not equal to 4 or it is non-alphabetic.
Note that there is a stub included. It was well worth the trouble to create it to properly
debug the main part of the program first. Then I could confidently debug the breakUp
function.
// File: ch10prg5.cc
// Text Replacement
#include <iostream>
#include <cctype>
void breakUp ( char line[], char list[][80], int & n );
// accepts line of up to 79 characters
// returns in list the succession of null terminated words
// and non-alpha character strings between them null terminated.
// returns number of words in list in parameter n
int main()
{
using namespace std;
char line[80];
char list[80][80]; // ample space for 80 words of max length 79
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
Page 22
int n;
cin.getline(line, 80);
breakUp( line, list, n);
int i = 0;
while ( i < n )
{
if ( 4 == strlen( list[i] ) && isalpha(list[i][0] ))
{
if ( isupper(list[i][0]) )
cout << "Love";
else if ( islower( list[i][0] ) )
cout << "love";
}
else
cout << list[i];
i++;
}
cout << endl;
}
void breakUp ( char line[], char list[][80], int & number )
{
// separator is anything not alpha
int i = 0, k = 0, wordNumber = 0;
while( i < strlen( line ) )
{
k = 0;
// after the start of the line, line[i] is non-alpha
// extract the next non-alphabetic substring
while ( !isalpha(line[i]) )
{
list[wordNumber][k] = line[i];
i++; k++;
}
list[wordNumber][k] = '\0';
wordNumber++;
k = 0;
// line[i] is an alphabetic character.
// extract the next alphabetic substring
while ( isalpha (line[i]) )
{
list[wordNumber][k] = line[i];
i++; k++;
}
list[wordNumber][k] = '\0';
wordNumber++;
}
number = wordNumber;
}
/*
//
//
//
STUB * STUB *STUB *STUB *STUB *STUB *STUB *STUB *
It is WELL WORTH the trouble to create this to get the rest of the
program running correctly.
Note that array initializers would have been better than this long
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
// drawn out
void breakUp
{
list[0][0]
list[0][1]
list[0][2]
list[0][3]
list[0][4]
collection of assignments.
( char line[], char list[][80], int & n)
=
=
=
=
=
' ';
' ';
' ';
' ';
'\0';
list[1][0]
list[1][1]
list[1][2]
list[1][3]
=
=
=
=
'n';
'o';
'w';
'\0';
list[2][0] = ' ';
list[2][1] = '\0';
list[3][0] = 'i';
list[3][1] = 's';
list[3][2] = '\0';
list[4][0] = ' ';
list[4][1] = '\0';
list[5][0] = 'a';
list[5][1] = '\0';
list[6][0] = ' ';
list[6][1] = '\0';
list[7][0]
list[7][1]
list[7][2]
list[7][3]
list[7][4]
=
=
=
=
=
'G';
'o';
'o';
'd';
'\0';
list[8][0] = ' ';
list[8][1] = '\0';
list[9][0]
list[9][1]
list[9][2]
list[9][3]
list[9][4]
list[10][0]
list[10][1]
list[10][2]
list[10][3]
list[10][4]
n = 11;
}
*/
=
=
=
=
=
't';
'i';
'm';
'e';
'\0';
=
=
=
=
=
' ';
' ';
' ';
' ';
'\0';
Page 23
Instructor’s Resource Manual for Savitch Absolute C++ 03/08/16
Chapter 9 Strings
5. Sexist Language
No solution is provided.
6. PD music CD convert index by title to index by composer
No solution is provided.
Page 24
Download