C++ Primer P.317 2010-3-22 wcdj
We'll close this section with a program to illustrate creating, searching, and iterating across a map. Our problem is to write a program that, given one string, transforms it into another. The input to our program is two files. The first file contains several word pairs. The first word in the pair is one that might be in the input string. The second is the word to use in the output. Essentially, this file provides a set of word transformations when we find the first word, we should replace it by the second. The second file contains the text to transform. If the contents of the word transformation file is
nah no
i I
sez said
tanx thanks
cuz because
…
and the text we are given to transform is
nah i sez tanx cuz i …
then the program should generate the following output:
no I said thanks because I …
[Solution]
Our solution, which appears on the next page, stores the word transformation file in a map, using the word to be replaced as the key and the word to use as the replacement as its corresponding value. We then read the input, looking up each word to see if it has a transformation. If so, we do the transformation and then print the transformed word. If not, we print the original word.
Our main program takes two arguments: the name of the word transformation file and the name of the file to transform. We start by checking the number of arguments. The first argument, argv[0], is always the name of the command. The file names will be in argv[1] and argv[2].
Once we know that argv[1] is valid, we call open_file to open the word transformation file. Assuming the open succeeded, we read the transformation pairs. We call insert using the first word as the key and the second as the value. When the while concludes, trans_map contains the data we need to transform the input. If there's a problem with the arguments, we throw an exception and exit the program.
Next, we call open_file to open the file we want to transform. The second while uses getline to read that file a line at a time. We read by line so that our output will have line breaks at the same position as our input file. To get the words from each line we use a nested while loop that uses an istringstream. This part of the program is similar to the sketch we wrote on page 257(in CN vresion).
The inner while checks each word to see if it is in the transformation map. If it is, then we replace the word by its corresponding value from the map. Finally, we print the word, transformed or not. We use the bool firstword to determine whether to print a space. If it is the first word in the line, we don't print a space.
{
// ok: let's display it
map<string, string>::iterator map_it = trans_map.begin();
//cout << "Here is our transformation map: /n/n";
while (map_it != trans_map.end()) {
cout << "key: " << map_it->first;
if (map_it->first.size() == 1)
cout << " ";
if (map_it->first.size() == 3)
cout << " ";
else if (map_it->first.size() == 4)
cout << " ";
else if (map_it->first.size() == 5)
cout << " ";
cout << "value: " << map_it->second << endl;
++map_it;
}
cout << "/n/n";
{ // this block just produces the vector so that we can print it
// for the book
cout << "Here is our original string input:/n/n";
// read some text to transform
ifstream input;
if (!open_file(input, argv[2]))
throw runtime_error("no input file");
string word;
while (getline(input, word))
cout << word << endl;
cout << "/n/n";
input.close(); input.clear();
}
}
// ok, now we're ready to do the transformations
// open the input file and check that the open succeeded
ifstream input;
if (!open_file(input,argv[2]))
{
throw runtime_error("no input file");
}
string line;// hold each line from the input
// read the text to transform it a line at a time
while (getline(input,line))
{
istringstream stream(line);// read the line a word at a time
string word;
bool firstword=true;// controls whether a space is printed
while (stream>>word)
{
// ok: the actual mapwork, this part is the heart of the program
map<string,string>::const_iterator map_it=trans_map.find(word);
// if this word is in the transformation map
if (map_it!=trans_map.end())
{
// replace it by the transformation value in the map
word=map_it->second;
}
if (firstword)
{
firstword=false;
}
else
{
cout<<" ";// print space between words
}
cout<<word;
}
cout<<endl;// done with this line of input
}
return 0;
}
ifstream& open_file(ifstream &in, const string &file)
{
in.close();// close in case it was already open
in.clear();// clear any existing errors
// if the open fails, the stream will be in an invalid state
in.open(file.c_str());// open the file we were given
return in;// condition state is good if open succeeded
}
/*
输出:
key: cuz value: because
key: i value: I
key: nah value: no
key: sez value: said
key: tanx value: thanks
Here is our original string input:
nah i sez tanx cuz i
cuz nah i sez tanx cuz i
no I said thanks because I
because no I said thanks because I
Press any key to continue
*/