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

Testing the Translator Class 281

This code simply manages the translation operations. The code consists of three basic functions:

The class manages an input file that contains the mappings of old text to the new text we

wish to replace it with. (See 1.)

This method takes a filename, attempts

to open it, and reads in the data pairs if the open operation was successful.

The class manages the storage of the mappings

in a hash table. (See 2.)

This is done by the hash table, represented by the Standard Template Library map class.

The class replaces a given string with the string

desired in the final output. (Shown at 3.) Note that we simply pass in each string and replace it if it is found in the hash table. If it is not, we return the original string. This allows the application to save time by not bothering to check if the string needs to be replaced

or not.

3.Save the source code as a file in your code editor.

This dictionary will do all the work of loading data from a translation file and replacing individual words with specified translated words. For example, consider the idea of replacing all occurrences of the word good with the word bad. If we were given the input string good day, I am having a good time at this goodly party, we would translate this into bad day, I am having a bad time at this goodly party. Note that we only replace full matches, not text that appears anywhere in the input string. The class now exists, the only thing we need to do is use it.

Testing the Translator Class

After you create the dictionary, you should create a test driver that not only ensures that your code is correct, but also shows people how to use your code.

The following steps show you how to create a test driver that illustrates various kinds of input from the user, and shows how the class is intended to be used:

1. In the code editor of your choice, reopen the source file to hold the code for your test program.

In this example, I named the test program ch47.cpp.

2. Append the code from Listing 47-2 into your file.

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

LISTING 47-2: THE TRANSLATOR CLASS TEST DRIVER

int main()

 

 

{

 

4

Translator t(“translate.txt”);

 

printf(“Enter some text to translate:

 

“);

 

 

string in;

 

 

bool bDone = false;

 

5

while ( !bDone )

 

{

 

char c;

 

 

cin.get(c);

 

 

if ( c == ‘\n’ )

 

 

bDone = true;

 

 

else

 

 

in += c;

 

 

}

 

 

printf(“Initial string: %s\n”,

 

 

in.c_str() );

 

 

// Break it down into words.

 

 

string word;

 

 

for ( int i=0; i<in.length(); ++i )

 

 

{

 

 

if ( in[i] == ‘ ‘ )

 

 

{

 

6

if ( word.length() )

 

{

 

string sOut = t.replace( word );

cout << sOut << “ “;

}

(continued)

282 Technique 47: Hashing Out Translations

LISTING 47-2 (continued)

word = “”;

 

 

}

 

 

else

 

 

word += in[i];

 

 

}

 

7

if ( word.length() )

 

{

 

string sOut = t.replace( word ); cout << sOut << “ “;

}

}

The code above simply reads input from the keyboard, breaks it down into words, and then calls the translator to replace any portions of the string that need to be translated from our input file. The translator operates on a file called translate.txt, as shown at 4. You can easily change this filename, or even pass one in on the command line, if you wish. Each line is read from the keyboard, one character at a time, until a car-

riage return is encountered. (See

5.) Finally, we

parse the input line until we encounter

a space

(shown at 6) or the end of the string (shown at 7). When this happens, we replace the “word” we have parsed by calling the replace method of the translator.

3. Save the source-code file in the editor and then close the editor application.

4. Compile the source file, using your favorite compiler on your favorite operating system.

5. In your code editor, create a translation-text file.

This file will contain the pairs of words to be used in the translation file; each pair consists of a word and the translated version of that word. I called mine translation.txt, but you can call it whatever you like.

6. Put the following text into the translation.txt file:

good bad insult dis talk jive

You can place any words you want in the file. The first word will be replaced by the second word in any sentence you input into the system.

7. Run the program on your favorite operating system.

If you have done everything properly, you should see output resembling this:

$ ./a.exe

Enter some text to translate: Initial string: you are so good to

talk and not insult me

you are so bad to jive and not dis me

48 Implementing

Virtual Files

Technique

Save Time By

Understanding virtual files

Creating a virtual file class

Testing your class

These days, application data can be very large and can consume a lot of your memory. Depending on your application footprint and target operating system, loading the entire application file into

memory at one time may be impossible. Memory shortages are common with embedded systems, and with hand-held devices, where only a limited amount of memory is available to share between numerous applications. There are a number of ways to handle such conditions, from reading in only as much data as you can and not storing the remainder, to limiting the data to chunks and forcing the user to select which chunk they want. None of these solutions, however, is quite as elegant to either the developer or the end-user as the virtual file. A virtual file is a window into the file you are trying to process. It appears to end-users as if they’re seeing the whole file at once — but they’re really seeing just one small piece of it at a time. If you build virtual views of your files into your application up front, you save time in the long run, because you won’t have to go back and redesign your applications when the files become larger than you were expecting.

The capability to provide a virtual window into a file not only conserves memory, it also conserves speed. By loading only a small chunk of the file at any given moment, you can load immense files in no time and all, and page through them very quickly. This method is used by many large text editors.

Creating a Virtual File Class

In order to manage virtual files, we will need two different classes. First, we will need a single class that manages a given chunk of data from the file. This class will manage the data in that chunk, as well as keep track of where that particular piece of data was read from the file and how big it is. After this, we need to have a manager that keeps track of all of those chunks of data, allocating new objects to manage the individual pieces that are read in, and deleting the pieces that are no longer used. Let’s create a few classes to do that now.

284

Technique 48: Implementing Virtual Files

 

1. In the code editor of your choice, create a new

2. Type the code given in Listing 48-1 into your

file to hold the code for the source file of the

file.

technique.

Better yet, copy the code from the source file on

In this example, the file is named ch48.cpp, although you can use whatever you choose. This file will contain the class definition for our virtual file manager objects.

this book’s companion Web site.

LISTING 48-1: THE VIRTUAL FILE MANAGER CLASSES

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

using namespace std;

class FileChunk

 

1

{

 

 

private:

 

 

 

string

_chunk;

 

 

long

_offset;

 

 

long

_length;

 

 

bool

_inuse;

 

 

long

_accesses;

 

 

protected:

 

 

 

void Clear()

{

_offset = -1; _length = -1; _chunk = “”; _inuse = false; _accesses = 0;

}

void Copy( const FileChunk& aCopy )

{

_offset = aCopy._offset; _length = aCopy._length; _chunk = aCopy._chunk; _inuse = aCopy._inuse;

}

bool Read ( ifstream& in, long pos, long length )

{

_offset = pos; _chunk = “”; _length = 0;

// Seek to the position in the stream. in.seekg( pos );

if ( in.fail() ) return false;

Creating a Virtual File Class 285

//Read up to the end of the file or the last of the length

//bytes.

for ( int i=0; i<length; ++i )

{

char c; in.get(c);

if ( in.fail() ) break;

_length ++; _chunk += c;

}

_inuse = true;

}

public:

FileChunk( void )

{

Clear();

}

FileChunk( ifstream& in, long pos, long length )

{

Clear();

Read( in, pos, length );

}

FileChunk( const FileChunk& aCopy )

{

Clear(); Copy( aCopy );

}

FileChunk operator=( const FileChunk& aCopy )

{

Clear(); Copy( aCopy ); return *this;

}

// Accessors long Offset()

{

return _offset;

}

long Length()

{

return _length;

}

string& Chunk()

{

_accesses ++; return _chunk;

}

bool InUse(void)

{

return _inuse;

(continued)

286 Technique 48: Implementing Virtual Files

LISTING 48-1 (continued)

}

void setOffset( long offset )

{

_offset = _offset;

}

void setLength( long length )

{

_length = length;

}

void setChunk( const string& chunk )

{

_chunk = chunk;

}

long AccessCount( void )

{

return _accesses;

}

bool Load( ifstream& in, long offset, long length )

{

Clear();

return Read( in, offset, length );

}

};

 

 

 

const int kChunkSize = 128;

 

 

class FileChunkManager

 

2

{

 

 

private:

 

 

 

int

_numChunks;

 

 

FileChunk

*_chunks;

 

 

ifstream

_in;

 

 

string

_fileName;

 

 

protected:

 

 

 

FileChunk *findChunk( long theOffset )

{

for ( int i=0; i<_numChunks; ++i )

{

if ( _chunks[i].InUse() == true )

{

long offset = _chunks[i].Offset(); long length = _chunks[i].Length();

if ( theOffset >= offset && theOffset <= offset+length ) return &_chunks[i];

}

}

return NULL;

}

FileChunk *addChunk( long theOffset )

Creating a Virtual File Class 287

{

for ( int i=0; i<_numChunks; ++i )

{

if ( _chunks[i].InUse() == false )

{

if ( _chunks[i].Load( _in, theOffset, kChunkSize ) ) return &_chunks[i];

}

}

return NULL;

}

FileChunk *getLeastRecentlyAccessed()

{

int idx = 0;

long access = _chunks[0].AccessCount();

for ( int i=0; i<_numChunks; ++i )

{

if ( _chunks[i].InUse() == true )

{

if ( _chunks[i].AccessCount() < access )

{

idx = i;

access = _chunks[i].AccessCount();

}

}

}

return &_chunks[idx];

}

public:

FileChunkManager(void)

{

_numChunks = 0; _chunks = NULL;

}

FileChunkManager( const char *fileName, int nMaxChunks )

{

_numChunks = nMaxChunks;

_chunks = new FileChunk[ nMaxChunks ]; _fileName = fileName;

_in.open( fileName );

}

FileChunkManager( const FileChunkManager& aCopy )

{

_numChunks = aCopy._numChunks;

_chunks = new FileChunk[ _numChunks ]; for ( int i=0; i<_numChunks; ++i )

_chunks[i] = aCopy._chunks[i]; _fileName = aCopy._fileName; _in.open( _fileName.c_str() );

}

(continued)

288 Technique 48: Implementing Virtual Files

LISTING 48-1 (continued)

virtual ~FileChunkManager( void )

{

delete [] _chunks;

}

char operator[]( long offset )

{

// Find which chunk this offset is in. FileChunk *chunk = findChunk( offset ); if ( chunk == NULL )

{

// There are none. See whether we can add one. chunk = addChunk( offset );

}

//If we have one, just get the data from it.

//Otherwise, we have to go dump one.

if ( !chunk )

{

chunk = getLeastRecentlyAccessed(); chunk->Load( _in, offset, kChunkSize );

}

// Finally, extract the piece we need. int pos = offset - chunk->Offset(); return chunk->Chunk()[pos];

}

// Dump the function to illustrate what is in the chunks. void Dump(void)

{

for ( int i=0; i<_numChunks; ++i )

{

printf(“Chunk %d: %s\n”, i, _chunks[i].InUse() ? “In Use” : “NOT Used” ); printf(“Offset: %ld\n”, _chunks[i].Offset() );

printf(“Length: %ld\n”, _chunks[i].Length() ); if ( _chunks[i].InUse() )

printf(“String: [%s]\n”, _chunks[i].Chunk().c_str() );

}

}

};

The code above shows the two basic classes: the

the FileChunkManager class, shown at 2, main-

FileChunk and FileChunkManager classes. The

tains an array of FileChunk objects, keeping

track

FileChunk class, shown at 1, manages a single

of which ones are in use and what their offsets are.

chunk of data in the file. This data includes the

When a block of the file is requested, the man-

offset within the file, the file object itself, and the

ager object looks through its list to see if there is

text from that location in the file. It also stores

a block that has that data in it and if so, requests

the length of the chunk that was actually read in,

that particular text from that particular object.

since some chunks at the end of the file could be

3. Save the source file in the code editor.

 

smaller than the full size block. The second class,