0

I have written dictionary.c using hashtable. The speller compiles,

and correctly finds misspelled words (I use a test version with 13-word

dictionary and a 14-word test text), but although the dictionary loads

all words correctly (tested), the speller.c doesn't recognize an apostrophe

and treats a word such as John's as two separate words John and s.

We can't alter speller.c in any way, hence please let me know if there is

a way around it. Below I post the shortened version of speller.c that I

used to test if the problem indeed is in speller.c and to see how it prints

words from the text (use printf as a test here), and below that I post

test text I used, and below that - the whole speller.c.

TEST SPELLER.c

#include <ctype.h>
#include <stdio.h>

#define TEXT "texts/test.txt"
int main(void)
{
    // try to open text
    char* text = TEXT;
    FILE* fp = fopen(text, "r");
    if (fp == NULL)
    {
        printf("Could not open %s.\n", text);
        return 1;
    }
    // prepare to spell-check
    int index = 0, words = 0;
    char word[45] = {0};

    // spell-check each word in text
    for (int c = fgetc(fp); c != EOF; c = fgetc(fp))
    {
        // allow only alphabetical characters and apostrophes
        if (isalpha(c) || (c == '\'' && index > 0))
        {
            // append character to word
            word[index] = c;
            index++;

            // ignore alphabetical strings too long to be words
            if (index > 45)
            {
                // consume remainder of alphabetical string
                while ((c = fgetc(fp)) != EOF && isalpha(c));

                // prepare for new word
                index = 0;
            }
        }

        // ignore words with numbers (like MS Word can)
        else if (isdigit(c))
        {
            // consume remainder of alphanumeric string
            while ((c = fgetc(fp)) != EOF && isalnum(c));

            // prepare for new word
            index = 0;
        }

        // we must have found a whole word
        else if (index > 0)
        {
            // terminate current word
            word[index] = '\0';

            // update counter
            words++;

            printf("%s\n", word);
            // prepare for next word
            index = 0;
        }
    }


    // close text
    fclose(fp);

    return 0;
}

TEST TEXT

Mother computer brain key music
cat milk strong paper ink John’s
saint fish zoo

UNABRIDGED SPELLER

/**
 * speller.c
 *
 * Computer Science 50
 * Problem Set 5
 *
 * Implements a spell-checker.
 */

#include <ctype.h>
#include <stdio.h>
#include <sys/resource.h>
#include <sys/time.h>

#include "dictionary.h"
#undef calculate
#undef getrusage

// default dictionary
#define DICTIONARY "dictionaries/large"

// prototype
double calculate(const struct rusage* b, const struct rusage* a);

int main(int argc, char* argv[])
{
    // check for correct number of args
    if (argc != 2 && argc != 3)
    {
        printf("Usage: speller [dictionary] text\n");
        return 1;
    }

    // structs for timing data
    struct rusage before, after;

    // benchmarks
    double time_load = 0.0, time_check = 0.0, time_size = 0.0, time_unload = 0.0;

    // determine dictionary to use
    char* dictionary = (argc == 3) ? argv[1] : DICTIONARY;

    // load dictionary
    getrusage(RUSAGE_SELF, &before);
    bool loaded = load(dictionary);
    getrusage(RUSAGE_SELF, &after);

    // abort if dictionary not loaded
    if (!loaded)
    {
        printf("Could not load %s.\n", dictionary);
        return 1;
    }

    // calculate time to load dictionary
    time_load = calculate(&before, &after);

    // try to open text
    char* text = (argc == 3) ? argv[2] : argv[1];
    FILE* fp = fopen(text, "r");
    if (fp == NULL)
    {
        printf("Could not open %s.\n", text);
        unload();
        return 1;
    }

    // prepare to report misspellings
    printf("\nMISSPELLED WORDS\n\n");

    // prepare to spell-check
    int index = 0, misspellings = 0, words = 0;
    char word[LENGTH+1];

    // spell-check each word in text
    for (int c = fgetc(fp); c != EOF; c = fgetc(fp))
    {
        // allow only alphabetical characters and apostrophes
        if (isalpha(c) || (c == '\'' && index > 0))
        {
            // append character to word
            word[index] = c;
            index++;

            // ignore alphabetical strings too long to be words
            if (index > LENGTH)
            {
                // consume remainder of alphabetical string
                while ((c = fgetc(fp)) != EOF && isalpha(c));

                // prepare for new word
                index = 0;
            }
        }

        // ignore words with numbers (like MS Word can)
        else if (isdigit(c))
        {
            // consume remainder of alphanumeric string
            while ((c = fgetc(fp)) != EOF && isalnum(c));

            // prepare for new word
            index = 0;
        }

        // we must have found a whole word
        else if (index > 0)
        {
            // terminate current word
            word[index] = '\0';

            // update counter
            words++;

            // check word's spelling
            getrusage(RUSAGE_SELF, &before);
            bool misspelled = !check(word);
            getrusage(RUSAGE_SELF, &after);

            // update benchmark
            time_check += calculate(&before, &after);

            // print word if misspelled
            if (misspelled)
            {
                printf("%s\n", word);
                misspellings++;
            }

            // prepare for next word
            index = 0;
        }
    }

    // check whether there was an error
    if (ferror(fp))
    {
        fclose(fp);
        printf("Error reading %s.\n", text);
        unload();
        return 1;
    }

    // close text
    fclose(fp);

    // determine dictionary's size
    getrusage(RUSAGE_SELF, &before);
    unsigned int n = size();
    getrusage(RUSAGE_SELF, &after);

    // calculate time to determine dictionary's size
    time_size = calculate(&before, &after);

    // unload dictionary
    getrusage(RUSAGE_SELF, &before);
    bool unloaded = unload();
    getrusage(RUSAGE_SELF, &after);

    // abort if dictionary not unloaded
    if (!unloaded)
    {
        printf("Could not unload %s.\n", dictionary);
        return 1;
    }

    // calculate time to unload dictionary
    time_unload = calculate(&before, &after);

    // report benchmarks
    printf("\nWORDS MISSPELLED:     %d\n", misspellings);
    printf("WORDS IN DICTIONARY:  %d\n", n);
    printf("WORDS IN TEXT:        %d\n", words);
    printf("TIME IN load:         %.2f\n", time_load);
    printf("TIME IN check:        %.2f\n", time_check);
    printf("TIME IN size:         %.2f\n", time_size);
    printf("TIME IN unload:       %.2f\n", time_unload);
    printf("TIME IN TOTAL:        %.2f\n\n",
           time_load + time_check + time_size + time_unload);

    // that's all folks
    return 0;
}

/**
 * Returns number of seconds between b and a.
 */
double calculate(const struct rusage* b, const struct rusage* a)
{
    if (b == NULL || a == NULL)
    {
        return 0.0;
    }
    else
    {
        return ((((a->ru_utime.tv_sec * 1000000 + a->ru_utime.tv_usec) -
                  (b->ru_utime.tv_sec * 1000000 + b->ru_utime.tv_usec)) +
                 ((a->ru_stime.tv_sec * 1000000 + a->ru_stime.tv_usec) -
                  (b->ru_stime.tv_sec * 1000000 + b->ru_stime.tv_usec)))
                / 1000000.0);
    }
}

Thank you!

  • speller.c is well-tested code and does handle apostrophes correctly. It is far more likely that there is a problem in either your load or check code. If you can't identify the problem, perhaps you can add your code to the question? – Cliff B Mar 16 '16 at 17:09
  • @Cliff thank you for your reply. I understand that speller.c is a well-tested program. No, the problem has nothing to do with my code, because (please, check my message - I have clearly stated that) this is speller.c that prints out input text (my test one) in such a strange manner - I did provide a shortened version of speller.c too, so if you can spare a minute to create test text with my words, you can test the speller.c piece I have provided - it will print all words and you will see that John's comes out as John and s. – Vitale Mar 17 '16 at 10:03
1

It's always fun when someone finds a really unique problem! Because you were so adamant that there was a problem, I went back and used your test data to prove there wasn't. I was surprised when it did what you said.

Well, there's sort of a problem. In this case, the problem is the input data and internationalization. As I said, the code is well tested and handles apostrophes correctly. The problem here is that " John’s " doesn't contain an apostrophe. It appears to be something else, a UTF-8 right single quotation mark. When I copied it and ran it through a converter, it returned a digital value of 8217 or hex 0x2019.

Speller is not set up to handle internationalized character codes, UTF-8, or anything similar, only standard ASCII characters. For starters, UTF-8 characters are too large for standard char storage. This particular symbol doesn't appear to be an alpha, a digit or an apostrophe to speller, so it treats it as an end of word, thus causing the behavior you are seeing. (I'm thinking that there are certain ASCII chars that will cause the same behavior, but you can test for that if you wish.)

The short fix is to use an actual apostrophe, " ' ", ASCII 39, or the single quote key next to the enter key on US keyboards and not " ’ ".

Somehow, I don't think it will be amended to handle international character sets, these psets aren't being written quite so rigorously.

At least, this is why you are seeing what you are seeing.

If this answers your question, please click on the check mark to accept. Let's keep up on forum maintenance. ;-)

| improve this answer | |
  • thank you very much for believing me and for taking time to test ) I wouldn't claim something until I am sure that what I say is correct. Thank you for your explanation. Interesting why the single quotation mark turned into some other character - I use the same button as usual, and that's an English language keyboard )) Anyway, thank you! – Vitale Mar 25 '16 at 16:43
  • Welcome to the wonderful world of internationalized programming. Don't know what's going on with your computer, but I'd suspect that it's quite probably configured to use UTF-8 encoding from your keyboard, or it's taking whatever your keyboard uses locally and mapping to UTF-8. Not uncommon outside the U.S. Glad I could figure it out. – Cliff B Mar 25 '16 at 16:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .