I'm having some trouble with the load section of pset5 on CS50, it would be great if someone could help. I'm trying to load a trie that reads from a dictionary (file fp below) and then iterates through the letters to create the trie.
I understand the concept of building a trie but I think I'm missing something with how the struct pointers are set up (hopefully I'm not way off the track with the code below). I've tried to set up 'trap' to navigate through each stage of the try.
I'm currently getting a segmentation fault so not entirely sure where to go next. Any help would be massively appreciated.
/**
* Loads dictionary into memory. Returns true if successful else false.
*/
bool load(const char* dictionary)
{
//create word node and set root
typedef struct node {
bool is_word;
struct node* children[27];
} node;
node* root = calloc(1, sizeof(root));
root -> is_word = false;
node* trav = root;
//open small dictionary
FILE* fp = fopen(dictionary, "r");
if (fp == NULL)
{
printf("Could not open %s.\n", dictionary);
return false;
}
//read characters one by one and write them to the trie
for (int c = fgetc(fp); c != EOF; c = fgetc(fp))
{
//set index using to lower. Use a-1 to set ' to 0 and other letters 1-27
int index = tolower(c)-('a'-1);
//if new line (so end of word) set is_word to true and return trav to root)
if (index == '\n')
{
trav->is_word = true;
trav = root;
}
//if trav-> children is NULL then create a new node assign to next
//and move trav to that position
if (trav->children[index] == NULL)
{
node* next = calloc(1, sizeof(node));
trav->children[index] = next;
trav = next;
}
//else pointer must exist so move trav straight on
else {
trav = trav->children[index];
}
}
fclose(fp);
return false;
}
I'm assuming you set the size of array children[] to store 26 letters of the alphabet plus apostrophes. If so, when fgetc(fp) returns an apostrophe with an acsii code of 39 (I think), index will be set to -57, which is definitely not part of trav->children. That's probably where you're getting the segfault (or at least one of the places)
.
Hope this helps.
Related
I need to start with the head node every cycle to add the new node in the right place. I think my current code makes the pointer for head and sptr equal so when I move one, the other one moves too. How do I move the pointer sptr to the beginning?
In debugger head->letter[1] turns true when I save an "a" as a word as it should, but later turns back to false as soon as sptr = head; runs. I think it has to do with the pointers.
typedef struct node
{
bool exist;
struct node* letter[28];
} trie;
trie *head = NULL;
int words = 0;
// Loads dictionary into memory, returning true if successful else false
bool load(const char *dictionary)
{
int i = 0;
FILE *infile = fopen(dictionary, "r");
if (infile == NULL)
{
printf("Could not open %s.\n", dictionary);
return 1;
}
// allocate memory
head = calloc(sizeof(trie), 1);
head->exist = false;
trie *sptr = head;
int cr;
// loop through file one character at a time
while ((cr = fgetc(infile)) != EOF)
{
// build a trie
// check if it's end of line
if (cr != 10)
{
i = tolower(cr) - 96;
// check for apostrophy
if (i < 0)
{
i = 0;
}
// check if the position exists
if (sptr->letter[i] == NULL)
{
sptr->letter[i] = malloc(sizeof(trie));
sptr->exist = false; // not the end of the word
}
sptr = sptr->letter[i];
}
else // indicate the end of a word that exists
{
sptr->exist = true;
sptr = head;// I think the problem might be here, I'm trying to move the pointer to the beginning.
words++;
}
}
return true;
}
Found the problem. It was in line sptr->exist = false, it should've read sptr->letter[i]->exist = false. The pointer was moving fine but I was changing the value of where the current pointer was, not the newly created node.
Any guidance would be appreciated. I personally believe the problem lies in the load method. Also, the basic functionality of each method is written in the comments. What could be the cause of my segmentation fault? and Is everything working as intended? Thank you for your time.
Any resources that may point in me in the proper direction would be appreciated too.
/**
* Implements a dictionary's functionality.
*/
#include <stdbool.h>
#include "dictionary.h"
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <cs50.h>
//Defining node:
typedef struct node
{ //Inner workings of each "element" in the linked lists
char word[LENGTH + 1]; //the word within the node is +1'd due to the memory after the word containing /0
struct node *next; //linked list
}node;
node *alphabetList[27]; //26 buckets that can contain variables of type node(of dynamic size)
//one bucket for each letter of the alphabet
node *cursor = NULL;
node *head = NULL;
/**
* Returns true if word is in dictionary else false.
*/
bool check(const char *word)
{
int bucketIndex ;
//no need to malloc information b/c we are simply pointing to previously established nodes.
if(word[0] >= 65 && word[0] < 97){
bucketIndex = word[0] - 65;
}
else{
bucketIndex = word[0] - 97;
}
node *head = alphabetList[bucketIndex];
node *cursor = head;
while(cursor != NULL)
{
cursor = cursor -> next;
if(strcmp(cursor -> word, word) != 0)
{
return true;
}
}
return false;
}
/**
* Loads dictionary into memory. Returns true if successful else false.
*/
bool load(const char *dictionary)
{
char *word = NULL;
int i = 0; //index
FILE *dictionaryTextFile;
dictionaryTextFile = fopen(dictionary, "r");
//scan for word
while(fscanf(dictionaryTextFile, "%s", word) != EOF)
{
//for every word we scan we want to malloc a node to ascertain we have sufficent memory
node *new_node = malloc(sizeof(node));
if(new_node == NULL) //error check(if you run out of memory malloc will return null)
{
unload();
return false;
}
//error check complete.
else{
strcpy(new_node -> word, word);
}
//not sure from here on
char first_letter = new_node[i].word[0]; //first letter of node word (confused on how to execute this properly)
first_letter = tolower(first_letter);
int index = first_letter - 97;
if(word){
for(node *ptr = alphabetList[index]; ptr!= NULL; ptr = ptr->next)
{
if(!ptr-> next){
ptr->next = new_node;
}
}
}
else
{
alphabetList[index] = new_node;
}
i++;
}
return true;
}
/**
* Returns number of words in dictionary if loaded else 0 if not yet loaded.
*/
unsigned int size(void)
{
return 0;
}
/**
* Unloads dictionary from memory. Returns true if successful else false.
*/
bool unload(void)
{
for(int i = 0; i <= 26; i++)
{
node *head = alphabetList[i];
node *cursor = head;
while(cursor != NULL)
{
node *temp = cursor;
cursor = cursor -> next;
free(temp);
}
}
return true;
}
The problem is obvious now you've said on which line the code crashes. Consider these lines...
char *word = NULL;
int i = 0; //index
FILE *dictionaryTextFile;
dictionaryTextFile = fopen(dictionary, "r");
//scan for word
while(fscanf(dictionaryTextFile, "%s", word) != EOF)
You've got 2 problems there. Firstly, you don't check that the call to fopen worked. You should always check that the value returned is not NULL.
Secondly, and the cause of the crash, is that word is still NULL - you don't allocate any space to hold a string in it. You might as well declare it the same as you declare it inside node so replace
char *word = NULL;
with
char word[LENGTH+1];
Speaking of node and to save you coming back with another crash later, you should always make sure you initialise all attributes of a struct. In this case new_node->next should be set to NULL as otherwise you'll come to check it later in your for loop (which looks fine BTW) and it might appear to point to a node, but it's pointing at some random place in memory and the code will crash.
(lines 43-56) I am attempting to implement load function for pset 5. I created a nested while loop, first one for iterating until the end of file and the other until end of each word. I created char *c to store whatever "string" I scan from dictionary, but when I compile
bool load(const char *dictionary)
{
//create a trie data type
typedef struct node
{
bool is_word;
struct node *children[27]; //this is a pointer too!
}node;
FILE *dptr = fopen(dictionary, "r");
if(dptr == NULL)
{
printf("Could not open dictionary\n");
unload();
return false;
}
//create a pointer to the root of the trie and never move this (use traversal *)
node *root = malloc(sizeof(node));
char *c = NULL;
//scan the file char by char until end and store it in c
while(fscanf(dptr,"%s",c) != EOF)
{
//in the beginning of every word, make a traversal pointer copy of root so we can always refer back to root
node *trav = root;
//repeat for every word
while ((*c) != '/0')
{
//convert char into array index
int alpha = ((*c) - 97);
//if array element is pointing to NULL, i.e. it hasn't been open yet,
if(trav -> children[alpha] == NULL)
{
//then create a new node and point it with the previous pointer.
node *next_node = malloc(sizeof(node));
trav -> children[alpha] = next_node;
//quit if malloc returns null
if(next_node == NULL)
{
printf("Could not open dictionary");
unload();
return false;
}
}
else if (trav -> children[alpha] != NULL)
{
//if an already existing path, just go to it
trav = trav -> children[alpha];
}
}
//a word is loaded.
trav -> is_word = true;
}
}
Error:
dictionary.c:52:23: error: multi-character character constant [-
Werror,-Wmultichar]
while ((*c) != '/0')
I think this means '/0' should be a single character, but I don't know how else I would check for end of the word!
I also get another error message saying:
dictionary.c:84:1: error: control may reach end of non-void function [-Werror,-Wreturn-type]
}
I've been playing with it for a while now, and it is frustrating. Please help, and if you find any additional bugs, I'll be glad!
You want '\0' (null terminating character) instead of '/0'.
Additionally, don't forget to return a bool at the end of your function !
I keep getting segfault for my load function.
bool load(const char *dictionary)
{
//create a trie data type
typedef struct node
{
bool is_word;
struct node *children[27]; //this is a pointer too!
}node;
//create a pointer to the root of the trie and never move this (use traversal *)
node *root = malloc(sizeof(node));
for(int i=0; i<27; i++)
{
//NULL point all indexes of root -> children
root -> children[i] = NULL;
}
FILE *dptr = fopen(dictionary, "r");
if(dptr == NULL)
{
printf("Could not open dictionary\n");
return false;
}
char *c = NULL;
//scan the file char by char until end and store it in c
while(fscanf(dptr,"%s",c) != EOF)
{
//in the beginning of every word, make a traversal pointer copy of root so we can always refer back to root
node *trav = root;
//repeat for every word
while ((*c) != '\0')
{
//convert char into array index
int alpha = (tolower(*c) - 97);
//if array element is pointing to NULL, i.e. it hasn't been open yet,
if(trav -> children[alpha] == NULL)
{
//then create a new node and point it with the previous pointer.
node *next_node = malloc(sizeof(node));
trav -> children[alpha] = next_node;
//quit if malloc returns null
if(next_node == NULL)
{
printf("Could not open dictionary");
return false;
}
}
else if (trav -> children[alpha] != NULL)
{
//if an already existing path, just go to it
trav = trav -> children[alpha];
}
}
//a word is loaded.
trav -> is_word = true;
}
//success
free(root);
return true;
}
I checked whether I properly pointed new pointers to NULL during initialization. I have three types of nodes: root, traversal (for moving), and next_node. (i.) Am I allowed to null point the nodes before mallocing them? (ii.) Also, how do I free 'next_node' if that node is initialized and malloced inside an if statement? node *next_node = malloc(sizeof(node)); (iii.) If I want to set the nodes as global variables, which ones should be global? (iv.) Lastly, where do I set global variables: inside the main of speller.c, outside its main, or somewhere else? That's alot of questions, so you don't have to answer all of them, but it would be nice if you could answer the answered ones! Please point out any other peculiarities in my code. There should be plenty. I will accept most answers.
The cause of segmentation fault is the pointer "c" which you have not allocated memory.
Also, in your program -
//scan the file char by char until end and store it in c
while(fscanf(dptr,"%s",c) != EOF)
Once you allocate memory to pointer c, c will hold the word read from file dictionary.
Below in your code, you are checking for '\0' character-
while ((*c) != '\0')
{
But you are not moving the c pointer to point to next character in the string read because of which this code will end up executing infinite while loop.
May you can try something like this-
char *tmp;
tmp = c;
while ((*tmp) != '\0')
{
......
......
//Below in the loop at appropriate place
tmp++;
}
I have this function "load" where I read words from a dictionary and put them in an hashtable of linked lists. When I try to read a line and save it in my new_node->text the compiler returns SEGMENTATION FAULT and I don't know why. The error apperars when I use strncpy.
#define HASHTABLE_SIZE 76801
typedef struct node
{
char text[LENGTH+1];
//char* text;
//link to the next word
struct node* next_word;
}
node;
node* hashtable[HASHTABLE_SIZE];
bool load(const char* dictionary)
{
FILE* file = fopen(dictionary,"r");
unsigned long index = 0;
char str[LENGTH+1];
if(file == NULL)
{
printf("Error opening file!");
return false;
}
while(! feof(file))
{
node * new_node = malloc(sizeof(node)+1000);
while( fscanf(file,"%s",str) > 0)
{
printf("The word is %s",str);
strncpy(new_node->text,str,LENGTH+1);
//strcpy(new_node->text,str);
new_node->next_word = NULL;
index = hash( (unsigned char*)new_node->text);
if(hashtable[index] == NULL)
{
hashtable[index] = new_node;
}
else
{
new_node->next_word = hashtable[index];
hashtable[index] = new_node;
}
n_words++;
}
//free(new_node);
}
fclose(file);
loaded = true;
return true;
}
Let's look at your code line by line, shall we?
while(! feof(file))
{
This is not the right way to use feof - check out the post Why is “while ( !feof (file) )” always wrong? right here on StackOverflow.
node * new_node = malloc(sizeof(node)+1000);
Hmm, ok. We allocate space for one node and 1000 bytes. That's a bit weird, but hey... RAM is cheap.
while( fscanf(file,"%s",str) > 0)
{
Uhm... another loop? OK...
printf("The word is %s",str);
strncpy(new_node->text,str,LENGTH+1);
//strcpy(new_node->text,str);
new_node->next_word = NULL;
index = hash( (unsigned char*)new_node->text);
Hey! Wait a second... in this second loop we keep overwriting new_node repeatedly...
if(hashtable[index] == NULL)
{
hashtable[index] = new_node;
}
else
{
new_node->next_word = hashtable[index];
hashtable[index] = new_node;
}
Assume for a second that both words hash to the same bucket:
OK, so the first time through the loop, hashtable[index] will point to NULL and be set to point to new_node.
The second time through the loop, hashtable[index] isn't NULL so new_node will be made to point to whatever hashtable[index] points to (hint: new_node) and hashtable[index] will be made to point to new_node).
Do you know what an ouroboros is?
Now assume they don't hash to the same bucket:
One of the buckets now contains the wrong information. If you add "hello" in bucket 1 first and "goodbye" in bucket 2 first, when you try to traverse bucket 1 you may (only because the linking code is broken) find "goodbye" which doesn't belong in bucket 1 at all.
You should allocate a new node for every word you are adding. Don't reuse the same node.