-3

Here is the specification for the code: You are to use the Word and Dictionary classes defined below and write all member functions and any necessary supporting functions to achieve the specified result.

The Word class should dynamically allocate memory for each word to be stored in the dictionary.

The Dictionary class should contain an array of pointers to Word. Memory for this array must be dynamically allocated. You will have to read the words in from the file. Since you do not know the "word" file size, you do not know how large to allocate the array of pointers. You are to let this grow dynamically as you read the file in. Start with an array size of 8, When that array is filled, double the array size, copy the original 8 words to the new array and continue.

You can assume the "word" file is sorted, so your Dictionary::find() function must contain a binary search algorithm. You might want to save this requirement for later - until you get the rest of your program running.

Make sure you store words in the dictionary as lower case and that you convert the input text to the same case - that way your Dictionary::find() function will successfully find "Four" even though it is stored as "four" in your Dictionary.

Here is my code so far.

#include <cstring>
#include <iostream>
#include <fstream>

using namespace std;


class Word
{
    char* word_;
public:
    Word(const char* text = 0);
    ~Word() { delete[] word_; word_ = nullptr; }
    const char* word() const;
};



Word::Word(const char* arg)
    : word_(new char[strlen(arg) + 1])
{
    strcpy(word_, arg);
}


const char* Word::word() const
{

    return word_;
}


class Dictionary
{
    Word** words_;
    unsigned int capacity_;   // max number of words Dictionary can hold 
    unsigned int numberOfWordsInDictionary_;
    void resize() {
        capacity_ = capacity_ * 2;
        cout << "Size = " << capacity_ << endl;
    };
    void addWordToDictionary(char* word) { words_ += *word; };
public:
    Dictionary(const char* filename);
    ~Dictionary() {
        delete[] words_; words_ = nullptr;
    };
    bool find(const char* word);


};

Dictionary::Dictionary(const char * filename)
    : words_(new Word*[8]), capacity_(8), numberOfWordsInDictionary_(0)
{
    ifstream fin(filename);

    if (!filename) {
        cout << "Failed to open file!" << endl;
    }

    char buffer[32];
    while (fin.getline(buffer, sizeof(buffer)))
    {
        if (numberOfWordsInDictionary_ == capacity_)
        {
            resize();
        }

        addWordToDictionary(buffer);

    }
}

bool Dictionary::find(const char * left)
{
    int last = capacity_ - 1,
        first = 0,
        middle;
    bool found = false;

    while (!found && first <= last) {
        middle = (first + last) / 2;
        if (strcmp(left, reinterpret_cast<char*>(words_[middle])) == 0) {
            found = true;
        }
        else if (left > reinterpret_cast<char*>(words_[middle]))
            last = middle - 1;
        else
            first = middle + 1;
    }

    return found;
}
;

bool cleanupWord(char x[] ) {
    bool lower = false;
    int i = 0;
    while (x[i]) {
        char c = x[i];
        putchar(tolower(c));
        lower = true;
    }
    return lower;
}



    int main()
    {

        char buffer[32];
        Dictionary Websters("words.txt");
        ifstream fin("gettysburg.txt");
        cout << "\nSpell checking " << "gettysburg.text" << "\n\n";
        while (fin >> buffer) {
            if (cleanupWord(buffer) == true) {
                if (!Websters.find(buffer)) {
                    cout << buffer << " not found in the Dictionary\n";
                }
            }
        }

        system("PAUSE");
    }

When I run the program it stops after outputting "spellchecking Gettysburg.txt" and I don't know why. Thank you!

  • How certain are you that "gettysburg.txt" was successfully opened? Search term: Working Directory. Also watch out for [the Rule of Three](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three). Both of your classes break it and this leads to a future disaster. – user4581301 Jan 15 '19 at 22:28
  • On second read, The Rule of Three's not so important here. Learn it anyway because you can't write good quality, non-trivial C++ without it. – user4581301 Jan 15 '19 at 22:32
  • Clearly it's not going into your while loop, mostly likely because the file failed to open. Always check that files open, there's just too many reasons why they might not. – john Jan 15 '19 at 22:51
  • 1
    BTW on a related point, this code is wrong, `if (!filename) { cout << "Failed to open file!" << endl; }` it should be `if (!fin) { cout << "Failed to open file!" << endl; }` or slightly more clearly `if (!fin.is_open()) { cout << "Failed to open file!" << endl; }` Who knows you might find that your dictionary file failed to open as well. – john Jan 15 '19 at 22:53

1 Answers1

1

The most likely cause of this problem is the text files have not been opened. Add a check with is_open to make sure they have been opened.

When using Relative Paths (any path that does not go all the way back to the root of the file system (and is an Absolute Path)), take care that the program is being run from the directory you believe it to be. It is not always the same directory as the executable. Search Term to use to learn more about this: Working Directory.

Now on to other reasons this program will not work:

void addWordToDictionary(char* word) { words_ += *word; };

is not adding words to the dictionary. Instead it is advancing the address at which words_ points by the numeric value of the letter at *word. This is extremely destructive as it loses the pointer to the buffer allocated for words_ in the constructor making delete[] words_; in the Dictionary destructor ineffective and probably fatal.

Instead you want to (Note I use want to with a bit of trepidation. What you really want to do is use std::vector and std::string, but I strongly suspect this would upset the assignment's marker)

  1. Dynamically allocate a new Word with new.
  2. Place this word in a free spot in the words_ array. Something along the lines of words_[numberOfWordsInDictionary_] = myNewWord;
  3. Increase numberOfWordsInDictionary_ by 1.

Note that the Words allocated with new must all be released in the Dictionary destructor. You will want a for loop to help with this.

In addition, I would move the

    if (numberOfWordsInDictionary_ == capacity_)
    {
        resize();
    }

from Dictionary to addWordToDictionary so that any time addWordToDictionary is called it is properly sized.

Hmmm. While we're at it, let's look at resize

void resize() {
    capacity_ = capacity_ * 2;
    cout << "Size = " << capacity_ << endl;
};

This increases the object's capacity_ but does nothing to allocate more storage for words_. This needs to be corrected. You must:

  1. Double the value of capacity_. You already have this.
  2. Allocate a larger buffer to hold the replacement of words_ with new.
  3. Copy all of the Words in words_ to the larger buffer.
  4. Free the buffer currently pointed to by words_
  5. Point words_ at the new, larger buffer.

Addendum

I haven't looked closely at find because the carnage required to fix the reading and storage of the dictionary will most likely render find unusable even if it does currently work. The use of reinterpret_cast<char*> is an alarm bell, though. There should be no reason for a cast, let alone the most permissive of them all, in a find function. Rule of thumb: When you see a reinterpret_cast and you don't know what it's for, assume it's hiding a bug and approach it with caution and suspicion.

In addition to investigating the Rule of Three mentioned in the comments, look into the Rule of Five. This will allow you to make a much simpler, and probably more efficient, dictionary based around Word* words_, where words_ will point to an array of Word directly instead of pointers to Words.

Community
  • 1
  • 1
user4581301
  • 33,082
  • 7
  • 33
  • 54