Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ Timesaving Techniques (2005) [eng].pdf
Скачиваний:
65
Добавлен:
16.08.2013
Размер:
8.35 Mб
Скачать

440 Technique 70: Converting Numbers to Words

1. First, we break the number down into the highest unit, in this case thousands. So, the first part of our given number produces the number 123, with a unit of thousand.

2. Next, we split off the hundreds. So, we have one hundred.

3. The next step is to look at the tens unit. If this number were zero, we would skip it. In this case, it is a two, so the number is twenty. An important exception here is the number one. In this case, we have to apply special English rules (i.e. eleven, twelve, thirteen) and skip the ones digit. So, because the second digit is a two, we now have one hundred twenty.

4. Finally, we look at the ones digit. In our case, it is a three, so we have one hundred twenty three.

5. Append the units from step 1: one hundred twenty three thousand.

6. Repeat for the next block. If we are under a thousand, we skip the units part. Put both blocks together to produce: one hundred twenty three thousand four hundred fifty six.

From an object-oriented design viewpoint, the process shows that its cases have some elements in common elements — as well as some elements that are discrete for different cases. This suggests that we have a common base class, and then derived classes that manage those discrete elements. Furthermore, we can build some of the elements from the base classes to create new extended classes, such as when we create thousands from ones and tens.

This technique shows you how to convert numbers into written English.

Always take a step back from the problem when you are trying to do an object-oriented design. Doing so gives you the opportunity to see the problem from a big-picture perspective, which often allows you to break it down into small components much more easily. When you see all the pieces, you can also usually see the overlap between them — which can be factored into your base classes.

You can save a lot of time in the long-run by getting the design right from the beginning. Understanding how all the pieces fit together is essential to getting that design right.

Creating the Conversion Code

The first step toward implementing the system is to create the base classes used to build the application. The following steps show you how. The base classes represent the number ranges we are going to use to parse the existing number into digits and convert those digits into words.

1. In the code editor of your choice, create a new file to hold the code for the implementation of source file.

In this example, the file is named ch70.cpp, although you can use whatever you choose.

2. Type the code from Listing 70-1 into your file.

Or better yet, copy the code from the source file on this book’s companion Web site.

LISTING 70-1: THE CONVERSION BASE CLASSES: SOURCE CODE

#include <vector> #include <string> #include <iostream>

using namespace std;

class RangeEntry

{

long _lMin; long _lMax;

long _lIncrement; public:

RangeEntry(void)

{

_lMin = 0; _lMax = 0; _lIncrement = 1;

Creating the Conversion Code 441

}

RangeEntry(long min, long max, long inc=1)

{

_lMin = min; _lMax = max;

_lIncrement = inc;

}

RangeEntry( const RangeEntry& aCopy )

{

_lMin = aCopy._lMin; _lMax = aCopy._lMax;

_lIncrement = aCopy._lIncrement;

}

RangeEntry operator=(const RangeEntry& aCopy )

{

_lMin = aCopy._lMin; _lMax = aCopy._lMax;

_lIncrement = aCopy._lIncrement; return *this;

}

// Accessors long Min()

{

return _lMin;

}

long Max()

{

return _lMax;

}

long Increment()

{

return _lIncrement;

}

bool InRange( long lVal)

{

if ( lVal >= Min() && lVal <= Max() ) return true;

return false;

}

virtual string getString(int iVal)

{

return “Unknown”;

}

};

class OnesRangeEntry : public RangeEntry

{

(continued)

442 Technique 70: Converting Numbers to Words

LISTING 70-1 (continued)

public:

OnesRangeEntry(void)

: RangeEntry(0,19,1)

{

}

virtual string getString(int iVal)

{

switch ( iVal )

{

case 1:

return string(“one”); case 2:

return string(“two”); case 3:

return string(“three”); case 4:

return string(“four”); case 5:

return string(“five”); case 6:

return string(“six”); case 7:

return string(“seven”); case 8:

return string(“eight”); case 9:

return string(“nine”); case 10:

return string(“ten”); case 11:

return string(“eleven”); case 12:

return string(“twelve”); case 13:

return string(“thirteen”); case 14:

return string(“fourteen”); case 15:

return string(“fifteen”); case 16:

return string(“sixteen”); case 17:

return string(“seventeen”); case 18:

return string(“eighteen”); case 19:

return string(“nineteen”);

}

return string(“”);

}

};

Creating the Conversion Code 443

class TensRangeEntry : public RangeEntry

{

public:

TensRangeEntry(void)

: RangeEntry(20,90,10)

{

}

virtual string getString(int iVal)

{

int iDigit = iVal / 10; switch ( iDigit )

{

case 1:

return string(“ten”); case 2:

return string(“twenty”); case 3:

return string(“thirty”); case 4:

return string(“forty”); case 5:

return string(“fifty”); case 6:

return string(“sixty”); case 7:

return string(“seventy”); case 8:

return string(“eighty”); case 9:

return string(“ninety”);

}

return string(“”);

}

};

class HundredsRangeEntry : public RangeEntry

{

public:

HundredsRangeEntry(void)

: RangeEntry(100,1000,100)

{

}

virtual string getString(int iVal)

{

OnesRangeEntry ore;

int iDigit = iVal / 100;

string s = ore.getString( iDigit ); s += “ hundred”;

(continued)

444 Technique 70: Converting Numbers to Words

LISTING 70-1 (continued)

return s;

}

};

class ThousandsRangeEntry : public RangeEntry

{

public:

ThousandsRangeEntry(void)

 

 

{

: RangeEntry(1000,999999,1000) 1

}

 

 

 

virtual string getString(int iVal)

 

 

{

HundredsRangeEntry hre;

 

 

 

 

 

 

TensRangeEntry tre;

 

 

 

OnesRangeEntry ore;

 

 

 

int iDigit = iVal / 1000;

 

 

 

int iNum = iDigit;

 

 

 

string s = “”;

 

2

 

if ( hre.InRange(iDigit) )

 

 

{

 

s += hre.getString( iDigit );

iDigit = iDigit - (( iDigit/100 ) * 100);

}

if ( hre.InRange(iNum) )

{

s += “ “;

s += tre.getString( iDigit );

iDigit = iDigit - (( iDigit/10 ) * 10);

}

if ( ore.getString( iDigit ).length() ) s += “ “;

s += ore.getString( iDigit ); s += “ thousand”;

return s;

}

};

These base classes “know” how to convert a single string into a series of words that describe a number. However, because there are differences for thousands, hundreds, and single digit values, we need a set of classes to do each of these. After we have created the three basic ones

(digits, hundreds, thousands), we can then parse any number up to one million. If we wanted to parse numbers over one million, of course, we would need to add a new class, and so forth for each further magnitude we want to handle.

Creating the Conversion Code 445

The important thing is how the higher level classes (thousand, for example) call the lower level classes (hundred, ones) to process the smaller numbers. For example, take a look at the ThousandsRangeEntry class. The class contains a

range value that it processes, numbers between

1000 and 999999 (shown at 1). Within the

getString method, which converts

the number

into a human readable string, the class then uses the hundred, ten, and one digit parsing classes to do its work (see lines beginning at 2). We don’t duplicate a lot of code and we don’t have to go searching through the code to see which piece broke when there is an exception. For example, if we wanted to properly hyphenate output strings (thirty-five, instead of thirty five) we would just modify the tens class.

3. Save the source code in your code editor.

The next step is to implement the processing object that gathers up all the individual conversions into the output text string.

4. Reopen the source file in the code editor.

5. Append the code from Listing 70-2 to the source file.

LISTING 70-2: THE CONVERSION CLASS: SOURCE CODE

class NumberToWords

{

private:

vector< RangeEntry *> _entries;

protected:

virtual void InitializeToDefaults()

{

_entries.insert( _entries.end(), new OnesRangeEntry() ); 3

_entries.insert( _entries.end(), new TensRangeEntry() );

_entries.insert( _entries.end(), new HundredsRangeEntry() ); _entries.insert( _entries.end(), new ThousandsRangeEntry() );

}

public:

NumberToWords( void )

{

InitializeToDefaults();

}

virtual ~NumberToWords(void)

{

vector< RangeEntry *>::iterator iter;

for ( iter = _entries.begin(); iter != _entries.end(); ++iter )

delete (*iter);

}

 

 

string Convert( int iVal )

 

 

{

 

 

string sRet = “”;

 

4

while ( iVal > 0 )

 

{

 

bool bFound = false;

vector< RangeEntry *>::iterator iter;

for ( iter = _entries.begin(); iter != _entries.end(); ++iter )

{

if ( (*iter)->InRange( iVal ) )

{

if ( sRet.length()

)

sRet += “ “; sRet += (*iter)-

>getString( iVal );

iVal = iVal - ( (iVal / (*iter)- >Increment()) * (*iter)- >Increment());

bFound = true; break;

}

}

if ( !bFound ) iVal = 10;

}

return sRet;

}

};