Search code examples
crecursionprefix

How can I integrate a prefix checker to find complete words based on file read


ISSUE: I'm able to search through the file and print words based on numbers entered by the user (text-message number conversion), buy I should also be able to find full words based on only a portion of the numbers entered, so ... 72 would return pa, ra, sa, and sc... which would find words in my file such as party, radio, sandwich, and scanner.

I've tried utilizing prefix functions, but I'm unable to integrate them properly. The startsWith function is an example.

The words are based on a numbered keypad such as: Lettered Phone Keypad

CODE:

#include <stdio.h>
#include <string.h>
#include <stdbool.h>


const char numbered_letters[10][5] = {"", "", "abc", "def", "ghi", "jkl",
                                      "mno", "pqrs", "tuv", "wxyz"};



bool startsWith(const char *pre, const char *str) {
    size_t lenpre = strlen(pre),
            lenstr = strlen(str);
    return lenstr < lenpre ? false : strncmp(pre, str, lenpre) == 0;
}

void isEqual(char input[]) {
    int i = 0;
    //input[strlen(input)] = 0;
    startsWith(input, input);
    printf("check:  %s\n", input);
    //creating file
    FILE *fp = fopen("words_alpha.txt", "r");
    char str[32]; /* Handles largest possible string in the list which is: dichlorodiphenyltrichloroethane*/
    //fseek(fp, 0, SEEK_SET);
    if (fp == NULL) {
        printf("Error! No file!\n");
    } else {
        //printf("Enter a number to be converted into a word-list");
        //scanf("%s", str);

        while (!feof(fp)) {
            fscanf(fp, "%s", str);
            i = strncmp(input, str, 32);
            if (i == 0) {
                printf("HIT:    %s \n", input);
                break;
            } else {
                printf("");
            }
            //if (strncmp(str, "hello", 32 ) == 0) { /*if strncmp finds the word and returns true, print */
            //  printf("%s\n", str);

        }
        //printf("%s\n", str);
        //compareNums(num);
    }
    fclose(fp);
}


void printWordsUtil(int number[], int curr_digit, char output[], int n) {

    // Base case, if current output word is prepared
    int i;
    if (curr_digit == n) {
        //printf("%s ", output);
        isEqual(output);
        return;

    }

    // Try all possible characters for current digit in number[]
    // and recur for remaining digits
    for (i = 0; i < strlen(numbered_letters[number[curr_digit]]); i++) {

        output[curr_digit] = numbered_letters[number[curr_digit]][i];
        printWordsUtil(number, curr_digit + 1, output, n);/* recursive call */


        if (number[curr_digit] == 0 || number[curr_digit] == 1)
            return;
    }
}

// A wrapper over printWordsUtil().  It creates an output array and
// calls printWordsUtil()
void printWords(int number[], int n) {
    char result[n + 1];
    result[n] = '\0';

    printWordsUtil(number, 0, result, n);


}

//Driver program
int main(void) {
    int number[] = {4, 3, 9};
    int n = sizeof(number) / sizeof(number[0]);
    printWords(number, n);
    return 0;
}

FILE USED: Words Alpha (A file with over 420k words)

Thank you for any guidance you can provide!


Solution

  • There were a few bugs.

    Your prefix function was okay. The strncmp you replaced it with was not.

    You had a break on a hit that stopped on the first word match, so subsequent ones were not shown.

    On a hit you were printing the prefix string instead of the word string.

    I've annotated your code with the bugs and fixed them [please pardon the gratuitous style cleanup]:

    #include <stdio.h>
    #include <string.h>
    #include <stdbool.h>
    
    const char numbered_letters[10][5] = { "", "", "abc", "def", "ghi", "jkl",
        "mno", "pqrs", "tuv", "wxyz"
    };
    
    bool
    startsWith(const char *pre, const char *str)
    {
        size_t lenpre = strlen(pre),
            lenstr = strlen(str);
    
        return lenstr < lenpre ? false : strncmp(pre, str, lenpre) == 0;
    }
    
    void
    isEqual(char input[])
    {
        int i = 0;
        int ilen = strlen(input);
    
        // input[strlen(input)] = 0;
    // NOTE/BUG: this is extraneous
    #if 0
        startsWith(input, input);
    #endif
        printf("check:  '%s'\n", input);
    
        // creating file
        FILE *fp = fopen("words_alpha.txt", "r");
    // NOTE/BUG: this needs to be one more to contain the nul terminater
    #if 0
        /* Handles largest possible string in the list which is:
            dichlorodiphenyltrichloroethane */
        char str[32];
    #else
        char str[33];
    #endif
    
        // fseek(fp, 0, SEEK_SET);
        if (fp == NULL) {
            printf("Error! No file!\n");
        }
        else {
            // printf("Enter a number to be converted into a word-list");
            // scanf("%s", str);
    
    // NOTE/BUG: although feof works here, it is considered _bad_ practice
    #if 0
            while (!feof(fp)) {
                fscanf(fp, "%s", str);
    #else
            while (1) {
                if (fscanf(fp, "%s", str) != 1)
                    break;
    #endif
    
    // NOTE/BUG: this is broken
    #if 0
                i = strncmp(input, str, 32) == 0;
    #endif
    // NOTE: this works and is simpler than startsWith (which also works)
    #if 0
                i = strncmp(input, str, ilen) == 0;
    #endif
    #if 1
                i = startsWith(input, str);
    #endif
    
                if (i) {
    // NOTE/BUG: we want the actual word to be printed and not just the prefix
    #if 0
                    printf("HIT:    %s\n", input);
    #else
                    printf("HIT:    %s\n", str);
    #endif
    // NOTE/BUG: this break stops on the _first_ word match in the list but we
    // want all of them
    #if 0
                    break;
    #endif
                }
                else {
                    //printf("");
                }
                // if (strncmp(str, "hello", 32 ) == 0) { /*if strncmp finds the word and returns true, print */
                // printf("%s\n", str);
    
            }
            // printf("%s\n", str);
            // compareNums(num);
        }
        fclose(fp);
    }
    
    void
    printWordsUtil(int numbers[], int curr_digit, char output[], int n)
    {
    
        // Base case, if current output word is prepared
        int i;
    
        if (curr_digit == n) {
            // printf("%s ", output);
            isEqual(output);
            return;
        }
    
    // NOTE: did some cleanup to understand what was going on -- [probably] not a
    // bug
        int numcur = numbers[curr_digit];
        const char *letters = numbered_letters[numcur];
        int letlen = strlen(letters);
    
        // Try all possible characters for current digit in number[]
        // and recur for remaining digits
        for (i = 0; i < letlen; ++i) {
            output[curr_digit] = letters[i];
            printWordsUtil(numbers, curr_digit + 1, output, n); /* recursive call */
            if ((numcur == 0) || (numcur == 1))
                break;
        }
    }
    
    // A wrapper over printWordsUtil().  It creates an output array and
    // calls printWordsUtil()
    void
    printWords(int number[], int n)
    {
        char result[n + 1];
    
    // NOTE/BUG: this will have garbage in elements 0 to (n - 1)
    // NOTE/BUG: result is not used otherwise
        result[n] = '\0';
    
        printWordsUtil(number, 0, result, n);
    
    }
    
    //Driver program
    int
    main(void)
    {
        int number[] = { 4, 3, 9 };
        int n = sizeof(number) / sizeof(number[0]);
    
        printWords(number, n);
        return 0;
    }