CS50 pset5 SPELLER - most basic words & substrings issues - cs50

I have been stuck in pset5 for a while now. No matter from which angle I look to my code I cannot fin out what is wrong with it. I set my number of buckets randomly to 1000. Can someone find out the problem?
Below is the reply I get from check50
:) dictionary.c, dictionary.h, and Makefile exist
:) speller compiles
:( handles most basic words properly
expected "MISSPELLED WOR...", not "MISSPELLED WOR..."
:) handles min length (1-char) words
:) handles max length (45-char) words
:) handles words with apostrophes properly
:) spell-checking is case-insensitive
:( handles substrings properly
expected "MISSPELLED WOR...", not "MISSPELLED WOR..."
:| program is free of memory errors
can't check until a frown turns upside down
these is what I've done:
const unsigned int N = 1000;
// Returns true if word is in dictionary else false
bool check(const char *word)
{
//convert *word to lowercase so that the hash function is case-insensitive
int length = strlen(word);
char copy[length + 1];
for (int i = 0; i < length; i++)
{
copy[i] = tolower(word[i]);
}
// create a variable to return hashed value of word
int index_check = hash(copy);
//create cursor to traverse the linked list
node *cursor = table[index_check];
//check if word is in the linked list
while (cursor != NULL)
{
if (strcasecmp(word, cursor->word) == 0)
{
return true;
}
cursor = cursor->next;
}
//return false if cursor->next = NULL has been reached
return false;
}
// Hashes word into a number
unsigned int hash(const char *word)
{
// Source of hash function: stackoverflow.com/questions/14409466/simple-hash-functions
unsigned int count;
unsigned int hashValue = 0;
for(count = 0; word[count] != '\0'; count++)
{
hashValue = word[count] + (hashValue << 6) + (hashValue << 16) - hashValue;
}
return (hashValue % N);
}
// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
// open dictionary file
FILE *f = fopen(dictionary, "r");
if (f == NULL)
{
printf("Dictionary could not be opened\n");
return false;
}
//initialize string as a buffer, to be used in next function, fscanf
char buffer[LENGTH + 1];
//loop to check whether end of file has been reached
while (fscanf(f, "%s", buffer) != EOF)
{
//read words from file into buffer
fscanf(f, "%s", buffer);
//allocate memory for a node and check if NULL
node *n = malloc(sizeof(node));
if (n == NULL)
{
printf("Could not allocate memmory (malloc *n)\n");
return false;
}
//copy "buffer" into the node created
strcpy(n->word, buffer);
//call hash function
int index = hash(buffer);
//check if it's the first word being inserted into that bucket
if (table[index] == NULL)
{
table[index] = n;
}
else
{
n->next = table[index];
table[index] = n;
}
size_dictionary++;
}
fclose(f);
return true;
}
// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{
node *cursor;
node *tmp;
// run thru all buckets
for(int i = 0; i < N; i++)
{
//check if bucket isn't NULL
if(table[i] != NULL)
{
cursor = table[i];
tmp = cursor;
while (tmp != NULL)
{
cursor = cursor->next;
free(tmp);
tmp = cursor;
}
}
}
return true;
}

The clue is in WORDS IN DICTIONARY. Only half the words in dictionary are loaded. That is because of the back-to-back fscanf in load.

tiagoK, I think I'm having the same issue here and been stuck with this exercice for days. What did you mean by " the loop should have been done with lenght + 1", where did you declare this string ? I've tried it on the strcasecmp field and the hash one but it doesn't change anything. Do you remember ?
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// Choose number of buckets in hash table
const unsigned int N = 17576;
// Hash table
node *table[N];
// Initialize new variables used in the program
unsigned int numberofwords;
unsigned int hashvalue;
// Returns true if word is in dictionary, else false
bool check(const char *word)
{
// Hash the word to determine its hash value
hashvalue = hash(word);
// Set cursor to the head of the linked list of the word
node* cursor = table[hashvalue];
// Traverse into the linked list comparing the word to find a correspondance while the cursor isn't pointing to null
while (cursor != NULL)
{
if (strcasecmp(cursor->word, word) == 0)
{
return true;
}
cursor = cursor->next;
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
// Inittialize a variable to keep track of the total of ASCII values
unsigned int sum = 0;
// Loop in all the letters in the word
for (int i = 0 ; i < strlen(word) ; i++)
{
// Ignore digits
if (isdigit(word[i]))
{
continue;
}
else if (isalpha(word[i]) || word[i] == ('\''))
{
// Check the first three character in the word while setting them lower letters in order to get they ASCII value
sum += tolower(word[0] - 'a');
sum += tolower(word[1] - 'a');
sum += tolower(word[2] - 'a');
}
}
// Divide the total of the three letters by the number of buckets
hashvalue = sum % N;
return hashvalue;
}
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
// Open the file
FILE* file = fopen(dictionary, "r");
char word[LENGTH + 1];
{
if (file != NULL)
{
// While dictionary doesn't return EOF, read strings from the file, one at the time
while (fscanf(file, "%s", word) != EOF)
{
// Create a new node for the word and copy it in the node
node *n = malloc(sizeof(node));
if (n == NULL)
{
return 1;
}
strcpy (n->word, "%s");
// Hash word to obtain a hash value
hashvalue = hash(word);
// Insert word into hash table depending if it's the first word or not
if (table[hashvalue] != NULL)
{
n->next = table[hashvalue];
}
else
{
n->next = NULL;
}
table[hashvalue] = n;
// Add one to the counter
numberofwords++;
}
fclose(file);
}
else
{
fclose(file);
perror("Loading error");
return 1;
}
}
// Return true
return true;
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
// Return the number of words we're keeping track of while loading the dictionary
return numberofwords;
}
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
// Check all the head of the hash table
for(int i = 0; i < N; i++)
{
// Create a temporary node to not lose the rest of the linked list
node* cursor = table[i];
// Set the cursor to the next node while the temporary value remains at the inital location, then free it before to move to cursor
while(cursor != NULL)
{
node* tmp = cursor;
cursor = cursor->next;
free(tmp);
}
}
return true;
}

Related

PSET5 (Speller) Valgrind Error: Valgrind tests failed

I failed to pass the Valgrind tests and couldn't figure out what went wrong with my code. It seems like the issue is in the load() function as the Valgrind tests pointed out at the malloc() line. Could anyone help me take a look? Any guidance would be appreciated. Thank you!
Here is my code:
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// TODO: Choose number of buckets in hash table
const unsigned int N = 100;
// Hash table
node *table[N];
int count =0;
// Returns true if word is in dictionary, else false
bool check(const char *word)
{
// TODO
int i = hash(word);
node *cursor = table[i];
if (table[i] == NULL)
{
return false;
}
else
{
while(cursor!= NULL)
{
if(strcasecmp(cursor->word, word) == 0)
{
return true;
}
else
{
cursor = cursor->next;
}
}
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
// TODO: Improve this hash function
int bucket;
if(word[1] != 0)
{
bucket = (((toupper(word[0])-'A') * (toupper(word[1]- 'A')))% 10 + (toupper(word[0])-'A'));
}
else
{
bucket = (((toupper(word[0])-'A') * (toupper(word[0])-'A'))%10 + (toupper(word[0])-'A'));
}
return bucket;
}
// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
// TODO 1
//open the dictionary
FILE *file = fopen(dictionary, "r");
if(file == NULL)
{
printf("Can't load the dictionary\n");
return false;
}
//read string from file one at a time
char word[LENGTH + 1];
for (int i=0; i < N; i++)
{
table[i] = NULL;
}
while(fscanf(file, "%s", word) != EOF)
{
node *n = malloc(sizeof(node));
//create a new node for each word
if(n == NULL)
{
unload();
return false;
}
strcpy(n->word, word);
n->next = NULL;
count++;
char *c = n->word;
int number = hash(c);
if (table[number] != NULL)
{
//point the new node to the first node existing in the table
n->next = table[number];
//point the header to the new node
table[number] = n;
}
else
{
//n->next = NULL;
table[number] = n;
}
}
fclose(file);
return true;
}
// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
// TODO
return count;
//return 0;
}
// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
for (int i = 0; i > N; i++)
{
node *cursor = table[i];
while(cursor != NULL)
{
node *tmp = cursor;
cursor = cursor->next;
free(tmp);
}
free(cursor);
}
// TODO
return true;
}
Here is what the Valgrind tests show:
Valgrind tests
c.99 is this line -> node *n = malloc(sizeof(node));
The problem is in unload. It doesn't free any nodes. Review this line carefully and critically, it contains the error.
for (int i = 0; i > N; i++)

CS50 speller pset 5 (accessed 1 byte that does not belong to me but can't find the byte)

After running my code through help50 Valgrind, I got the following error message:
==6830== Invalid read of size 1
==6830== at 0x4C33614: strcasecmp (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6830== by 0x401176: check (dictionary.c:52)
==6830== by 0x400CD9: main (speller.c:112)
Looks like you're trying to access 1 byte of memory that isn't yours? Did you try to index into an array beyond its bounds? Take a closer look at line 52 of dictionary.c.
I think it has something to do with my check function but line 52 is just an if statement and I can't figure out where I'm trying to access that 1 byte from.**
My code is below:
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <strings.h>
#include "dictionary.h"
// Represents a node in a hash table
typedef struct node
{
char word[LENGTH + 1];
struct node *next;
}
node;
// Number of buckets in hash table
const unsigned int N = 1000;
//Number of words
unsigned int noWords = 0;
//Variable to check if dictionary loaded
bool isLoaded = false;
// Hash table
node *table[N];
// Returns true if word is in dictionary else false
bool check(const char *word)
{
//Changing letters to lower case because case insensitive
//Copy created because word argument is a constant. copy can be edited
int n = strlen(word) + 1;
char copy[LENGTH + 1];
for (int i = 0; i < n; i++)
{
copy[i] = tolower(word[i]);
}
// Add null terminator to end string
copy[n] = '\0';
//Hash the word to convert it to index and check if it's in any of the linked lists
int index = hash(copy);
if (table[index] != NULL) //Check if word is in linked list
{
for (node *cursor = table[index]; cursor != NULL; cursor = cursor -> next)
{
if (strcasecmp(cursor -> word, copy) == 0)
{
return true;
}
}
}
return false;
}
// Hashes word to a number
unsigned int hash(const char *word)
{
//Taken from http://www.cse.yorku.ca/~oz/hash.html (by djb2)
unsigned long h = 5381;
int c;
while ((c = *word++))
{
c = tolower(c);
h = (((h << 5) + h) + c) % N; /* hash * 33 + c*/
}
return h;
}
// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
char word[LENGTH + 1];
//Open dictionary
FILE *f = fopen(dictionary, "r");
//Check if file can be opened
if (f == NULL)
{
printf("%s\n", "File cannot be opened!");
return false;
}
//Read strings from file
while (fscanf(f, "%s", word) != EOF)
{
noWords++;
node *newNodePointer = malloc(sizeof(node));
if (newNodePointer == NULL)
{
unload();
printf("Out of memory");
return false;
}
int index = hash(word);//hashtable is an array of linked list. index helps insert node into hashtable
strcpy(newNodePointer -> word, word);//Copies word from infile into new node's word field
if (table[index] == NULL)//Check if same word already exists in the bucket
{
newNodePointer -> next = NULL;
table[index] = newNodePointer;
}
else
{
newNodePointer -> next = table[index];
table[index] = newNodePointer;
}
free(newNodePointer);
}
fclose(f);
isLoaded = true;
return true;
}
// Returns number of words in dictionary if loaded else 0 if not yet loaded
unsigned int size(void)
{
if (isLoaded)
{
return noWords;
}
return 0;
}
// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{
//Check if there's even a loaded dictionary
if (!isLoaded)
{
return false;
}
//Iterate through hashtable
for (int i = 0; i < N; i++)
{
if (table[i] != NULL)
{
node *cursor = table[i];
while (cursor != NULL)
{
node *tmp = table[i]; //tmp pointer continues pointing at table[i] while cursor points at next item in hashtable
cursor = cursor -> next;
free(tmp);
}
}
}
return true;
}
The problem is from here in load: free(newNodePointer);. It just released the memory where the word and the next pointer are stored!

Why can‘t I read the input text file?

I try to read the name of a file using scanf but failed.
I am very bad at pointers and could not find the problem.
Is there a problem with the pointer to the array of string?
Here is my code:
int* Read_file(char* str[])
{
FILE* fp = fopen(str[1], "r");
if(fp == NULL)
{
printf("File cannot open\n");
return NULL;
}
int rows = 0;
while(!feof(fp))
{
if(fgetc(fp) == '\n')
{
rows ++;
}
}
rows ++;
int* keys = (int*)malloc(3 * rows * sizeof(int));
fseek(fp, 0L, 0);
while(!feof(fp))
{
for(int i = 0;i < rows;i ++)
{
for(int j = 0;j < 3;j ++)
{
fscanf(fp,"%d", &keys[(3 * i) + j]);
}
}
}
fclose(fp);
return keys;
}
int main()
{
char* str[20];
printf("Build_tree ");
scanf("%s",&str);
int* keys = Read_file(str);
return 0;
}
Okay, so the thing is:
You need a char array to store a string(file-name). So you should use a char array. Instead, you were using an array of char pointers.
An array is actually a series of memory blocks. The name of the array represents a pointer to the first element of the array(in this case the first char variable).
While reading a string, scanf needs a location to store it. So you need to give it the address of the first char variable of your char array, which is available in your char array itself. So you have to pass str only to scanf. In the case of normal int,float, and such fundamental data types, their names represent memory blocks and not pointers to memory blocks, and hence you had to use a &.
Then for fopen, fopen expects a char*(which points to the first character of the char array stoing the filename) and you have to provide it with a char* . So you should pass str.
I think your code should go like
int* Read_file(char str[])
{
FILE* fp = fopen(str, "r");
if(fp == NULL)
{
printf("File cannot open\n");
return NULL;
}
int rows = 0;
while(!feof(fp))
{
if(fgetc(fp) == '\n')
{
rows ++;
}
}
rows ++;
int* keys = (int*)malloc(3 * rows * sizeof(int));
fseek(fp, 0L, 0);
while(!feof(fp))
{
for(int i = 0;i < rows;i ++)
{
for(int j = 0;j < 3;j ++)
{
fscanf(fp,"%d", &keys[(3 * i) + j]);
}
}
}
fclose(fp);
return keys;
}
int main()
{
char str[20];
printf("Build_tree ");
scanf("%s",str);
int* keys = Read_file(str);
//Whatever you want to do with the keys
return 0;
}
Comment for any queries.

PSET5 Speller Huge Number of Misspelled Words

I have been running the solution but is getting a high number of misspelled words.
WORDS MISSPELLED: 15904 as compared to staff's WORDS MISSPELLED: 955
Other than that, the word count is accurate and the runtime is alright.
I suspect the problem might come from the check / load function but I am not sure what caused it.
Some other code implemented a "to lowercase" in the check function, but I thought the (strcasecmp) would have done the job of comparing between strings, disregarding the case.
Check Function
bool check(const char *word)
{
int hashInt = hash(word);
if (table[hashInt] == NULL)
{
return 1;
}
node *cursor = table[hashInt];
while (cursor != NULL)
{
int i = strcasecmp(cursor -> word, word);
if (i == 0)
{
return 0;
break;
}
cursor = cursor -> next;
}
return false;
}
Load Function
bool load(const char *dictionary)
{
FILE *file = fopen(dictionary, "r");
if (file == NULL)
{
printf("error opening file");
return 1;
}
char word [LENGTH + 1];
while (fscanf(file, "%s\n", word) != EOF)
{
int hashInt = hash(word);
node *n = malloc(sizeof(node));
if (n == NULL)
{
unload();
return 1;
}
if (table[hashInt] == NULL)
{
table[hashInt] = n;
}
else
{
n -> next = table[hashInt];
table[hashInt] = n;
}
strcpy(n -> word, word);
wordLoaded++;
}
fclose(file);
return true;
}
Hash Function
unsigned int hash(const char *word)
{
unsigned int hash = 0;
for (int i = 0, n = strlen(word); i < n; i++)
hash = (hash << 2) ^ word[i];
return hash % N;
return 0;
}
From the spec:
dictionary is assumed to be a file containing a list of lowercase
words
and
Your implementation of check must be case-insensitive.
This int i = strcasecmp(cursor -> word, word); looks like it would fulfill the requirement as long as this int hashInt = hash(word); was also case insensitive. Alas, it is not; hash("A") and hash("a") will return different values.

Compare individual chars in strings contained within adjacent linked list nodes

I'm fairly new to C. I'm trying to create a program that will accept names of websites and usernames until the user inputs EOF. As the data is input it is stored in a linked list and the list is then alphabetically sorted as it's entered based on the name of the website. I'm unable to find a way to get the "Website" strings out of two nodes and compare each character to determine where a new node should be inserted. Any help would be greatly appreciated, I've tried searching this site and others for a solution. Was I being too ambitious? It's compiling but returning this fault after two entries.
test.c:68:24: runtime error: null pointer passed as argument 1, which is
declared to never be null
/usr/include/string.h:130:14: note: nonnull attribute specified here
Segmentation fault
This just happens to be the point at which I received the least errors. I realise string t & s shouldn't be NULL but any changes I make seem to make it worse.
My code is
#include <cs50.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
typedef struct node
{
char *website;
char *username;
int sitelen;
int userlen;
struct node *next;
}
node;
int main(void)
{
// memory for numbers
node *list = NULL;
// Prompt for numbers (until EOF)
while (true)
{
// Prompt for number
char *site = get_string("Website: ");
// Check for EOF
if (strcmp(site, "") == 0)
{
break;
}
char *user = get_string("Username: ");
// Check whether website is already in list
bool found = false;
for (node *ptr = list; ptr != NULL; ptr = ptr->next)
{
if (ptr->website == site)
{
found = true;
printf("Website already present");
break;
}
}
// If number not found in list, add to list
if (!found)
{
// Allocate space for number
node *nw = malloc(sizeof(node));
if (!nw)
{
return 1;
}
// Add details to list
nw->website = site;
nw->username = user;
nw->next = NULL;
int m = strlen(site);
if (list)
{ // if list head of list exists
node *pre = NULL; // pre pointer
string t = NULL;
strcpy(t, nw->website); // t is newly entered website in lowercase string
for (node *ptr = list; ptr != NULL; pre = ptr, ptr = ptr->next)
{ // move along linked list
string s = NULL;
strcpy(s, ptr->website); // t is newly entered website in lowercase string
if (ptr != list && !ptr->next)
{ // end of list, place new node
ptr->next = nw;
break;
}
else
{ // compare websites to see if node remains or ptr moves on
for (int y = m + 1, i = 0; i < y;)
{ // if a website starts the same but is shorter it is inserted
if (ptr == list && t[i] < s[i])
{
nw->next = list;
list = nw;
break;
}
else if (t[i] < s[i])
{
nw->next = ptr->next;
pre->next = nw;
break;
}
else if (t[i] == s[i])
{
i++;
}
else
{
break;
}
}
break;
}
}
}
else
{ // first itteration assigns list to nw
list = nw;
}
}
}
node *ptr = list;
while (ptr != NULL)
{
node *next = ptr->next;
free(ptr);
ptr = next;
}
}

Resources