I've got a problem with reading words from file and passing it to binary tree. When I debug it, it says:
Unhandled exception at 0x76E7773B(ntdll.dll) in Projekt.exe: 0.C00000005:
Access violation reading location 0x0037902A.
Here is the source code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
typedef struct Tree {
int val;
char *word;
struct Tree *left;
struct Tree *right;
} Tree;
void show(Tree *hd) {
if (hd != NULL) {
show(hd->left);
show(hd->right);
printf("%s -- %d\n", hd->word, hd->val);
}
}
void zero(Tree *aTree) {
if (aTree == NULL)
return;
zero(aTree->left);
free(aTree);
zero(aTree->right);
}
int alpha(char *word1, char *word2) {
if (word1[0] == 0 && word2[0] == 0)
return 2;
else
if (word1[0] == word2[0])
return alpha(&word1[1], &word2[1]);
else
if (word1[0] < word2[0])
return 1;
else
return 0;
}
Tree *create(char *word) {
Tree *temp;
temp = (Tree*)malloc(sizeof(Tree));
temp->left = temp->right = NULL;
temp->val = 1;
temp->word = (char*)malloc(sizeof(char));
strcpy(temp->word, word);
return temp;
}
Tree *insert(Tree *aTree, char *word) {
if (aTree == NULL) {
aTree = create(word);
} else
if (alpha(aTree->word, word) == 0) {
aTree->left = insert(aTree->left,word);
} else
if (alpha(aTree->word, word) == 1) {
aTree->right = insert(aTree->right, word);
} else
if (alpha(aTree->word, word) == 2) {
aTree->val++;
}
return aTree;
}
int main(int argc, char *argv[]) {
Tree *myTree = NULL;
char buffer[256] = { 0 };
char temp = 0;
int i = 0;
FILE *fp = fopen(argv[1], "r");
if (fp) {
while (temp != EOF) {
temp = getc(fp);
temp = toupper(temp);
if (temp >= 65 && temp <= 90) {
buffer[i] = temp;
i++;
} else {
if (buffer[0] != 0) {
puts(buffer);
myTree = insert(myTree, buffer);
memset(buffer, 0, sizeof(buffer));
i = 0;
}
}
}
}
fclose(fp);
show(myTree);
return 0;
}
Your program has several problems:
in function zero, you free the pointer too soon, you should move the free(aTree); as the last statement, otherwise you invoke undefined behavior, possibly a crash (but not the one you have, since you never call this function):
void zero(Tree *aTree) {
if (aTree != NULL) {
zero(aTree->left);
zero(aTree->right);
free(aTree);
}
In function alpha, you use recursion where a simple loop would suffice. The compiler may convert this to a loop, but it does have to. This is not a bug but why not use a more idiomatic approach such as:
int alpha(const char *word1, const char *word2) {
for (size_t i = 0;; i++) {
if (word1[i] == '\0' && word2[i] == '\0')
return 2;
if (word1[i] == word2[i])
continue;
if (word1[i] < word2[i])
return 1;
else
return 0;
}
}
In function create, you allocate a single byte for the string, this is definitely a cause for the crash. You should allocate strlen(word) + 1 or use strdup(word). You should not cast the return value of malloc() either:
Tree *create(const char *word) {
Tree *temp;
temp = malloc(sizeof(Tree));
temp->left = temp->right = NULL;
temp->val = 1;
temp->word = strdup(word);
return temp;
}
In function insert you call alpha multiple times, this is inefficient: you could use a switch statement:
Tree *insert(Tree *aTree, const char *word) {
if (aTree == NULL) {
return create(word);
switch (alpha(aTree->word, word)) {
case 0:
aTree->left = insert(aTree->left, word);
break;
case 1:
aTree->right = insert(aTree->right, word);
break;
case 2:
aTree->val++;
break;
}
}
return aTree;
}
function main has multiple issues:
You do not check if argv[1] is provided to the program. It would be NULL if the program is run without a command line argument.
Your test for end of file is incorrect: temp should be defined as int and you should test its value after reading the byte from the file with getc(), it is idiomatic to name c a variable used for this.
You should use character literals instead of hard coded ASCII values.
the test if (c >= 'A' && c <= 'Z') would work for ASCII, which is almost universal today, but it is more reliable to use isupper(c) instead.
You do not need to clear the buffer, setting a '\0' at the end before inserting the word is enough.
You should also check for buffer overflow and refuse to handle words longer than 255 characters.
You should not call fclose(fp) when fp is NULL, this is undefined behavior.
Here is a corrected version:
int main(int argc, char *argv[]) {
Tree *myTree = NULL;
char buffer[256];
int c;
size_t i;
FILE *fp;
if (argc < 2) {
printf("missing argument\n");
return 2;
}
fp = fopen(argv[1], "r");
if (fp == NULL) {
printf("cannot open %s\n", argv[1]);
return 1;
}
i = 0;
while ((c = getc(fp)) != EOF) {
c = toupper(c);
if (isupper(c)) {
if (i < sizeof(buffer))
buffer[i] = c;
i++;
} else {
if (i > 0 && i < sizeof(buffer)) {
buffer[i] = '\0';
puts(buffer);
myTree = insert(myTree, buffer);
i = 0;
}
}
}
fclose(fp);
show(myTree);
return 0;
}
Related
I am doing the Advent of Code, and I am trying to do it all in C. I am currently on day three, and I kind of solved it, but as the title says it behaves very strangely in my IDE CLion. Here is the objective.
I would very much like to know why it is not running properly, and finding out why appears to be beyond my capability.
This is my code:
//
// Created by gusta on 2022-12-06.
//
#include "aocio.h"
#include <string.h>
int cexists(char* cp, char c, int length);
char getDuplicate(char* line);
int main() {
FILE* fptr = openFile("../Inputs/day3.txt");
char* line;
while (readLine(fptr, &line)) {
char c = getDuplicate(line);
putchar(c);
}
return 0;
}
char getDuplicate(char* line) { // Returnerar STRING_END om ingen fanns
unsigned int length = strlen(line);
char letters[length/2];
char* cp = &letters[0];
for (int i = 0; i < length/2; i++) {
letters[i] = ' ';
}
for (int index = 0; line[index] != STRING_END; index++) {
if (index < length/2) {
int i_char = cexists(letters, line[index], length/2);
if (i_char == -1) {
*cp = line[index];
cp++;
}
}
else {
if (cexists(letters, line[index], length/2) != -1) {
return line[index];
}
}
}
}
int cexists(char* cp, char c, int length) {
for (int i = 0; i < length; i++) {
if (cp[i] == c) {
return i;
}
}
return -1;
}
Here is aocoi.h (advent of code input output.h):
//
// Created by gusta on 2022-12-01.
//
#ifndef ADVENTOFCODE_AOCIO_H
#define ADVENTOFCODE_AOCIO_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define SIGN_ASCII 45
#define TRUE 1
#define FALSE 0
#endif //ADVENTOFCODE_AOCIO_H
#define STRING_END '\0'
#define NUMBERS_ASCII 48
char* prompt(const char* question) {
printf(question);
char* string = malloc(1);
int curChar = 0, index = 0;
while (curChar != '\n') {
curChar = getchar();
if (curChar == '\n'){
string[index] = STRING_END;
}
else{
if (index > 0) {
string = (char*) realloc(string, index+1);
}
string[index] = curChar;
}
index++;
}
return string;
}
FILE* openFile(const char* fileName) {
FILE *fptr;
fptr = fopen(fileName, "r");
if (fptr == NULL) {
printf("Big fat file error!!\n");
fclose(fptr);
getchar();
exit(-1);
}
return fptr;
}
char readLine(FILE* fptr, char** line) {
int index = 0, end = 0;
char* string = (char *) malloc(1);
char curChar;
do {
end = fscanf(fptr, "%c", &curChar);
if (end == EOF) {
string[index] = STRING_END;
fclose(fptr);
*line = string;
return FALSE;
}
if (curChar == '\n') {
string[index] = STRING_END;
*line = string;
return TRUE;
}
else {
if (index > 0) {
string = (char *) realloc(string, index + 1);
}
string[index] = curChar;
index++;
}
} while (end != EOF);
}
int parseToInt(char* string) {
int numberLength = 0, number = 0;
int sign = FALSE;
for (int index = 0; string[index] != STRING_END; index++) {
numberLength++;
}
for (int index = numberLength-1; index >= 0; index--) {
char curChar = string[index];
if (index == 0 && curChar - SIGN_ASCII == 0) {
sign = TRUE;
continue;
}
if (curChar - NUMBERS_ASCII >= 0 && curChar - NUMBERS_ASCII <= 9) {
int num = (int) (curChar - NUMBERS_ASCII);
num *= (int)(pow(10, numberLength-index-1));
number += num;
}
else {
printf("Felaktig inmatning. parseInt kan bara ta in tal"); // Invalid input. parseInt can only take in numbers
getchar();
exit(-1);
}
}
if (sign == TRUE) {
number *= -1;
}
return number;
}
Through searching the web I found that the error code should mean stack overflow (ha!) or something like that, but I cannot for the life of me find any place in my code where that could occur. All pointers and stuff should be automatically freed - is there something I have missed?
In readLine():
if (index > 0) {
string = (char *) realloc(string, index + 1);
}
string[index] = curChar;
After this section, the buffer has size index+1, therefore string[index] is the last element you can write to. Afterwards, you do
index++;
Now, writing to string[index] is out of bounds, resulting in a buffer overflow. This is what happens when an EOF or EOL is detected.
I'm trying to find all the words with capital letters in a string, but am unable to process my data structure. i seem to be able to print out fileContent, indicating that it is loading in successfully, but my second function is not working on the file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* loadFile(char* fileName)
{
FILE *inputFile;
inputFile = fopen(fileName, "r");
//finds the end of the file
fseek(inputFile, 0, SEEK_END);
//stores the size of the file
int size = ftell(inputFile);
//Sets the scan to the start of the file
fseek(inputFile, 0, SEEK_SET);
char *documentStore = (char*)malloc(size);
int i = 0, c;
while((c = fgetc(inputFile)) != EOF)
{
documentStore[i] = c;
i++;
}
return documentStore;
}
void countImportantWords(char* fileContent, char** importantWords, int* frequencyWords)
{
int uniqueWordCount = 0;
int lengthWordStore = 10;
int i = 0;
int recording = 0;
char wordBuffer[50];
int wordBufferCount = 0;
int isWordPresent = 0;
while(fileContent[i] != EOF)
{
//To allocate more memory incase the structure is full
if(uniqueWordCount == lengthWordStore)
{
lengthWordStore += 10;
char** newWordStore = realloc(importantWords, lengthWordStore * sizeof(char*));
int* newFrequencyStore = realloc(frequencyWords, sizeof(int));
importantWords = newWordStore;
frequencyWords = newFrequencyStore;
}
printf("%s", wordBuffer);
//Conditions to fill if its a word
if(fileContent[i] >= 'A' && fileContent[i] <= 'Z' && recording == 0)
{
wordBuffer[0] = fileContent[i];
recording = 1;
}else if(fileContent[i] >= 'a' && fileContent[i] <= 'z' && recording == 1)
{
//each if is to check if the end of word is reached. Any character that is non alphabetical is considered end of word
wordBufferCount += 1;
wordBuffer[wordBufferCount] = fileContent[i];
} else if (fileContent[i] >= 'A' && fileContent[i] <= 'Z' && recording == 1)
{
wordBufferCount += 1;
wordBuffer[wordBufferCount] = fileContent[i];
} else {
//Adding a terminating character so that it strcpy only copies until that point
wordBuffer[wordBufferCount + 1] = '\0';
recording = 0;
//check to see if that word is in the array already, and if it is, it will just increment the frequency
for(int j = 0; j < uniqueWordCount; j++){
if(strcmp(wordBuffer, importantWords[j]) == 0)
{
frequencyWords[j] += 1;
isWordPresent = 1;
}
}
//if its not present, it should assign it to the structure
if(isWordPresent == 0)
{
char* wordStore = (char*)malloc(wordBufferCount * sizeof(char));
strcpy(wordStore, wordBuffer);
uniqueWordCount += 1;
importantWords[uniqueWordCount] = wordStore;
frequencyWords[uniqueWordCount] = 1;
}
}
i++;
}
}
int main() {
char fileName[50];
char *fileContent;
char **importantWords = (char**)malloc(10*sizeof(char**));
int *frequencyWords = (int*)malloc(10*sizeof(int));
printf("Please input the full file path: ");
scanf("%s", fileName);
fileContent = loadFile(fileName);
countImportantWords(fileContent, importantWords, frequencyWords);
int i = 0;
while(importantWords[i] != '\0')
{
printf("%s %d", importantWords[i], frequencyWords[i]);
i++;
}
return 0;
}
I've put in the full file so you can see how the structure was created incase that it is the issue, but ideally what would happen is that the final loop would print out all the words that are important and they're frequency. Currently i'm getting exit code 11, which i'm not sure what it means, but may be worth mentioning. I'd really appreciate any help :)
You can simplify the process dramatically but utilising functions and learning to manage your memory. I wrote a short example which does not take punctuation into account. It just assumes every word is separated by a space, which you can customise to your discretion.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char* readfile(char* filename){
char* data = NULL;
FILE* file = fopen(filename, "r");
if(file == NULL){
return NULL;
}
fseek(file, 0, SEEK_END);
long size = ftell(file)+1;
fseek(file, 0, SEEK_SET);
data = (char*)malloc(size);
if(data == NULL){
return NULL;
}
fgets(data, (int)size, file);
return data;
}
typedef struct uppercase_t{
char** word;
int count;
}uppercase;
void copy(uppercase* u,char* token){
size_t length = strlen(token);
u->word[u->count] = (char*)malloc(length+1);
if(u->word[u->count] == NULL){
return;
}
strcpy(u->word[u->count], token);
++u->count;
}
void createuppercasedata(uppercase* u, char* data){
const char delimeter[] = " ";
char* token = strtok(data, delimeter);
if(token == NULL){
return;
}
u->word = (char**)malloc(u->count+1);
if(u->word == NULL){
return;
}
if(isupper(token[0])){
copy(u,token);
}
while(token != NULL){
token = strtok(0, delimeter);
if(token != NULL)
if(isupper(token[0])) {
char** reallocated = (char**)realloc(u->word, u->count+1);
if(reallocated == NULL){
return;
}
u->word = reallocated;
copy(u, token);
}
}
}
void destroyuppercasedata(uppercase* u){
for(int index = 0; index < u->count; ++index){
free(u->word[index]);
}
free(u->word);
}
int main(){
char filename[] = "textfile";
char* data = readfile(filename);
if(data == NULL){
return -1;
}
uppercase u = {0};
createuppercasedata(&u, data);
printf("found %i uppercase words\n",u.count);
for(int index = 0; index < u.count; ++index){
printf("%s\n", u.word[index]);
}
destroyuppercasedata(&u);
free(data);
}
The code will allocate a new pointer for each uppercase and memory for the word to be copied too. It will free all the memory it allocated in the structure with destroyuppercasedata and it will free the initial data that was read from file. Error checking and memory management in C is really important. So utilise those properly.
This was the test file I used.
textfile
How many Uppercase words can Be Found In this text File the answer should be Seven
And this was the output to the terminal:
How
Uppercase
Be
Found
In
File
Seven
I have to create a binary search tree for an assignment that sorts strings. I ran debug mode and found where the issue for the segmentation fault is but I have no idea what the issue is or how to fix it. I get
#0 0x0000000000400969 in Insert (node=0x7fffffffe008, data=0x7fffffffe010 "run\n", c_flag=0) at testbst.c:38
#1 0x0000000000400df8 in main (argc=1, argv=0x7fffffffe188) at testbst.c:156"
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
typedef struct treeNode
{
char *data;
int count;
struct treeNode *left;
struct treeNode *right;
} treeNode;
void Insert(treeNode** node,char* data,int c_flag)
{
if(node==NULL)
{
treeNode *temp;
temp = (treeNode *)malloc(sizeof(treeNode));
temp -> data = data;
printf("%sdata", data);
printf("\n%stemp", temp -> data);
temp -> left = NULL;
temp -> right = NULL;
*node = temp;
return;
}
if(c_flag == 1)
{
if(caseCompare((*node)->data, data) == 1)
Insert(&(*node)->left, data, c_flag);
if(caseCompare((*node)->data, data) == -1)
Insert(&(*node)->right, data, c_flag);
else
(*node)->count++;
}
else if(compare((*node)->data, data) == 1)
Insert(&(*node)->left, data, c_flag);
if(compare((*node)->data, data) == -1)
Insert(&(*node)->right, data, c_flag);
else
(*node)->count++;
}
void PrintInorder(treeNode *node)
{
if(node==NULL)
{
return;
}
PrintInorder(node->left);
printf("%d ",node->data);
PrintInorder(node->right);
}
void DeletePostorder(treeNode *node)
{
if(node != NULL)
{
DeletePostorder(node->left);
DeletePostorder(node->right);
if(node->left!=NULL)
free(node->left);
if(node->right != NULL)
free(node->right);
free(node);
}
}
int compare(char* string1, char* string2)
{
int str1 = strlen(string1);
int str2 = strlen(string2);
int bigger;
int i = 0;
if(str1 > str2)
bigger = str1;
if(str1 < str2)
bigger = str2;
for(i = 0; i < bigger; i++)
{
if(tolower(string1[i]) > tolower(string1[i]))
return 1; //returns 1 if string 1 is farther letter
if(tolower(string1[i]) < tolower(string2[i]))
return -1; //returns -1 if string 1 letter is behind
}
return 0; //returns 0 if same words
}
int caseCompare(char* string1, char* string2)
{
int str1 = strlen(string1);
int str2 = strlen(string2);
int bigger;
int i = 0;
if(str1 > str2)
bigger = str1;
if(str1 < str2)
bigger = str2;
for(i = 0; i < bigger; i++)
{
if(string1[i] > string1[i])
return 1; //returns 1 if string 1 is farther letter
if(string1[i] < string2[i])
return -1; //returns -1 if string 1 letter is behind
}
return 0; //returns 0 if same words
}
int main(int argc, char **argv)
{
extern char *optarg;
extern int optind;
FILE* infile = stdin;
FILE* outfile = stdout;
char string[100];
treeNode* tree = NULL;
int cflag = 0, oflag = 0, c, err;
static char usage[] = "usage: %s [-c] [-o output_file_name] [input_file_filename]\n";
while ((c = getopt(argc, argv, "co:")) != -1)
switch (c)
{
case 'c':
cflag = 1;
break;
case 'o':
oflag = 1;
outfile = fopen(optarg, "w");
break;
case '?':
err = 1;
printf("%s\n", usage);
break;
}
if((argc - optind) > 1)
{
printf("Input Incorrect \n%s\n", usage);
exit(0);
}
if(argv[optind] != NULL)
if((infile = fopen(argv[optind], "r")) == NULL)
{
printf("File \"%s\" does not exist\nType in your sentences\n", argv[optind]);
infile = stdin;
}
while(fgets(string, sizeof(string), infile) != NULL && string[0] != '\n')
{
printf("%s", string);
Insert(&tree, string, cflag);
}
PrintInorder(tree);
DeletePostOrder(tree);
}
So I got this file and i want to scanf() only the digits inside the first {} than the digits inside the second {} and so on.
I've managed to call just the digits from the file, but I don't know how to separate them into groups
this is the file:
{5, 2, 3}, {1,5}, { }, { }, {3}, { }, { }
Below is the code I use so far
void main()
{
int rc=0,num,size;
FILE* f = fopen("graph-file.txt","rt");
if (f == NULL)
{
printf("Failed to open the file\n");
}
size = fscanf(f,"%d",&num);
fseek(f,1,SEEK_CUR);
while(rc != EOF)
{
if( rc == 1)
{
printf("%d\n",num);
}
fseek(f,1,SEEK_CUR);
rc = fscanf(f,"%d",&num);
}
}
Your code as it is has some issues, for example the mode string is wrong "rt"? You only need to specify "b" for binary and that only affects the end of line character which can be a problem if the file is read/written on different platforms.
To achieve what you want there is no simple way, as #JonathanLeffler suggests in this comment you could use a json library json-c1 is a very easy to use one.
If you want to do it yourself, try this code that I did just write
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int
append_number(int size, int **arrays, int value)
{
void *pointer;
pointer = realloc(arrays[0], (++size + 1) * sizeof(**arrays));
if (pointer == NULL)
return 0;
arrays[0] = pointer;
arrays[0][size] = value;
return 1;
}
int
get_value(const char *input)
{
if (input == NULL)
return -1;
while ((*input != '\0') && (isspace((unsigned char) *input) != 0))
input++;
if (*input == '\0')
return -1;
return atoi(input);
}
int *
extract_arrays(char *array)
{
int value;
int *list;
list = malloc(sizeof(*list));
if (list == NULL)
return NULL;
list[0] = 0;
while (array != NULL)
{
char *delimiter;
delimiter = strchr(array, ',');
if (delimiter != NULL)
*delimiter = '\0';
value = get_value(array);
if (value > 0)
list[0] += append_number(list[0], &list, value);
if (delimiter != NULL)
array = delimiter + 1;
else
array = NULL;
}
return list;
}
void
print_array(int *list)
{
fprintf(stdout, "[");
for (int j = 1 ; j < list[0] ; ++j)
fprintf(stdout, "%d ", list[j]);
if (list[0] > 0)
fprintf(stdout, "%d", list[list[0]]);
fprintf(stdout, "]\n");
}
int **
parse_line(char *line, size_t *count)
{
char *open;
char *close;
char *next;
int **arrays; // Depends on the maximum size of an inner array
*count = 0;
arrays = NULL;
next = line;
while ((next != NULL) && ((open = strchr(next, '{')) != NULL))
{
close = strchr(open, '}');
if (close != NULL)
{
void *pointer;
char *values;
*close = '\0';
next = strchr(close + 1, ',');
values = open + 1;
pointer = realloc(arrays, (*count + 1) * sizeof(*arrays));
if (pointer == NULL)
goto error;
arrays = pointer;
arrays[(*count)++] = extract_arrays(values);
}
else
next = open + 1;
}
return arrays;
error:
for (size_t i = 0 ; i < *count ; ++i)
free(arrays[i]);
free(arrays);
*count = 0;
return NULL;
}
int main(void)
{
char line[100];
size_t count;
int **arrays;
FILE *file;
file = fopen("graph-file.txt", "r");
if (file == NULL)
return -1; // Failure openning the file
while (fgets(line, sizeof(line), file) != NULL)
{
arrays = parse_line(line, &count);
for (size_t i = 0 ; i < count ; ++i)
{
print_array(arrays[i]);
// DO something with it ...
free(arrays[i]);
}
free(arrays);
}
fclose(file);
return 0;
}
Of course, there are a lot of possible optimizations (specially the realloc() parts), but I leave that to you.
Above, the int ** pointer returned by parse_line() contains count arrays where the first element is the length of each array.
1I know that on most linux distributions it can be installed with the package manager, and I have been using it a lot recently for some web development related projects.
Arrays are probably what you're going to need to accomplish this task. Arrays allow you to have various amounts of digits that you can store what you read into. That's how you can separate the groupings. The next part will be how to change which part of the array you're looking at, which this link should also help with.
C Arrays Tutorial.
If the numbers that you're trying to read will only be single digit numbers, then you could ditch the fseek and fscanf functions and use getc instead. Just check each read for anything that's not a number '0'-'9'.
C getc
Those websites I linked also have a lot of other good tutorials on them for learning C\C++.
Good luck.
Edit: less condescending.
Incorrect code to check if a word can be made of smaller given words (word break).This is the code I wrote for the above mentioned problem, however an online judge declares it as incorrect, what could be the possible reasons? And how should I modify my code?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Node structure */
typedef struct node {
int letter[26];
struct node* next[26];
int is_word;
} node;
/* Create node */
node* getnode(void) {
node* p = malloc(sizeof(node));
int i;
for (i = 0; i < 1004; i++) {
p->letter[i] = 0;
p->next[i] = NULL;
}
p->is_word = 0;
return p;
}
/* make dictionary */
void fill_dictionary(char word[], node* start) {
int len = strlen(word), i;
node* temp = start;
for (i = 0; i < len; i++) {
if (temp->letter[word[i] % 'a'] == 0) {
temp->letter[word[i] % 'a'] = 1;
temp->next[word[i] % 'a'] = getnode();
temp = temp->next[word[i] % 'a'];
} else {
temp = temp->next[word[i] % 'a'];
}
}
temp->is_word = 1;
return;
}
int spell_check(char line[100003], node* start) {
int len = strlen(line), i, flag = 0;
node* temp = start;
for (i = 0; i < len; i++) {
if (temp->letter[line[i] % 'a'] == 0) {
return 1;
} else {
temp = temp->next[line[i] % 'a'];
flag = 0;
if (temp->is_word == 1) {
flag = 1;
temp = start;
}
}
}
if (flag == 1) {
return 0;
} else {
return 1;
}
}
int main(void) {
int n, i, ans, m;
scanf("%d %d", &n,&m); // no. of words in dictionary
node* start = getnode();
for (i = 0; i < n; i++) {
char word[11]; // max length of dictionary word
scanf("%s", word);
fill_dictionary(word, start);
}
scanf("%d", &n); // no. of lines to be checked
for (i = 0; i < n; i++) {
char line[100003]; // max length of a line
scanf("%s", line);
ans = spell_check(line, start);
if (ans == 0) {
printf("YES\n");
} else {
printf("NO\n");
}
}
return 0;
}
Here's one way to to it. This compiles and runs. It displays the parsed result. It tries to read the dictionary from a file called "dictionary.text" in the current directory. You can change it to put the dictionary wherever you want. I commented it heavily to help you understand it but it has some subtle C things you may need to really think about and figure out. One bit of advice: Name everything in a program as extremely accurately for what it is/does as possible (but reasonably succinct). That will help immensely when trying to debug or figure out what you did wrong. Careless names really make code confusing and hard to debug.
Good luck!
Example:
$ gcc -o wordsplitter wordsplitter.c
$ wordsplitter xyzhellogoodbyefoodogcatpigcarwhereareyouhorse
xyz "hello" "goodbye" foo "dog" "cat" pigcar "where" "are" "you" horse
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DICTIONARY_FILEPATH "dictionary.txt"
#define MAX_WORD_SIZE 100
/*
* Error codes (usually this is put in a header file and included)
*/
#define SUCCESS 0
#define FILE_NOT_FOUND -1
#define OUT_OF_MEMORY -2
typedef struct word {
struct word *next;
char *word;
} word_t;
word_t *dictionaryListhead = NULL;
typedef struct wordsubcomponent {
struct wordsubcomponent *next;
char *text;
int isDictionaryWord;
} wordsubcomponent_t;
int
loadDictionaryFromFile(char *filename, word_t **listhead)
{
char wordFromFile[MAX_WORD_SIZE];
word_t *lastWordStored = NULL;
FILE *dictionaryFile = fopen(filename, "r");
if (dictionaryFile == NULL) {
return FILE_NOT_FOUND;
}
while(fgets(wordFromFile, sizeof(wordFromFile), dictionaryFile)) {
word_t *newDictionaryWordNode;
if ((newDictionaryWordNode = calloc(sizeof(word_t), 1)) == NULL) { // calloc automatically zeroes memory
return OUT_OF_MEMORY;
}
char *cp = strchr(wordFromFile, '\n');
if (cp != NULL)
*cp = '\0'; // get rid of trailing \n
newDictionaryWordNode->word = strdup(wordFromFile);
if (*listhead == NULL) {
lastWordStored = *listhead = newDictionaryWordNode;
} else {
lastWordStored = lastWordStored->next = newDictionaryWordNode;
}
}
fclose(dictionaryFile);
return SUCCESS;
}
wordsubcomponent_t
*newsubcomponent() {
wordsubcomponent_t *subcomp = NULL;
if ((subcomp = calloc(sizeof(wordsubcomponent_t), 1)) != NULL) {
subcomp->text = strdup(""); // seed with empty string (instead of NULL) so we can append
} else {
fprintf(stderr, "out of memory (fatal). program exiting\n");
exit(-1);
}
return subcomp;
}
/*
* Returns an linked list of word subcomponents for the given word, split up around dictionary words
*/
wordsubcomponent_t *getWordSubcomponents(char *wordToParse, word_t *listhead) {
wordsubcomponent_t *subcomponents, *currSubcomp;
subcomponents = currSubcomp = newsubcomponent();
for (char *cp = wordToParse; cp < wordToParse + strlen(wordToParse);) { // exit when cp gets to end of word to parse.
int matchFlag = 0;
for (word_t *wordNode = listhead; wordNode != NULL; wordNode = wordNode->next) {
if (strncasecmp(cp, wordNode->word, strlen(wordNode->word)) == 0) { // prefix of cur. ptr is dict word.
if (strlen(currSubcomp->text) != 0) // Detected non-dict text in subcomp.
currSubcomp = currSubcomp->next = newsubcomponent(); // leave in list & add new subcomp for dict word.
currSubcomp->text = wordNode->word; // save dict-word in subcomp
currSubcomp->isDictionaryWord = 1;
currSubcomp = currSubcomp->next = newsubcomponent(); // dict-word in list, so get new subcomp
cp += strlen(wordNode->word); // advance cp past extracted dict-word
matchFlag = 1;
break; // break out of inner-loop
}
}
if (!matchFlag) { // No dict-word found at cp
char oneNullTerminatedLetter[2] = { *cp++, '\0' }; // put 1st ltr into NULL-terminated string & adv cp.
strcat(currSubcomp->text, oneNullTerminatedLetter); // append letter-as-string to curr subcomp
}
}
return subcomponents;
}
void
dumpDictionary(word_t *listhead) {
printf("\nList of dictionary words:\n");
printf("----------------\n");
for (word_t *wordNode = listhead; wordNode != NULL; wordNode = wordNode->next) {
printf(" %s\n", wordNode->word);
}
printf("----------------\n\n");
}
int
main(int argc, char **argv)
{
int status;
if ((status = loadDictionaryFromFile(DICTIONARY_FILEPATH, &dictionaryListhead)) < 0) {
switch(status) {
case FILE_NOT_FOUND:
fprintf(stderr, "Error accessing dictionary: %s\n", argv[0]);
break;
case OUT_OF_MEMORY:
fprintf(stderr, "Out of memory");
break;
}
return EXIT_FAILURE;
}
/*
* Load dictionary first so we can show them the list of words if they didn't
* pass in a command line argument with the word to parse.
*/
if (argc < 2) {
fprintf(stderr, "Usage: %s <word_to_parse>\n\n", argv[0]);
dumpDictionary(dictionaryListhead);
return EXIT_FAILURE;
}
wordsubcomponent_t *subcomp = getWordSubcomponents(argv[1], dictionaryListhead);
while(subcomp != NULL && strlen(subcomp->text) > 0) {
if (subcomp->isDictionaryWord)
printf("\"%s\" ", subcomp->text);
else
printf("%s ", subcomp->text);
subcomp = subcomp->next;
}
printf("\n");
return EXIT_SUCCESS;
}
#nerdist colony:
There is a resource leak in loadDictionaryFromFile. This means a file pointer was not closed when returning from this function in case of an error.
Here is a corrected copy of this function
int loadDictionaryFromFile(char *filename, word_t **listhead)
{
char wordFromFile[MAX_WORD_SIZE];
word_t *lastWordStored = NULL;
FILE *dictionaryFile = fopen(filename, "r");
if (dictionaryFile == NULL) {
return FILE_NOT_FOUND;
}
while(fgets(wordFromFile, sizeof(wordFromFile), dictionaryFile)) {
word_t *newDictionaryWordNode;
if ((newDictionaryWordNode = calloc(sizeof(word_t), 1)) == NULL) { // calloc automatically zeroes memory
fclose(dictionaryFile); // <-- Close the file pointer
return OUT_OF_MEMORY;
}
char *cp = strchr(wordFromFile, '\n');
if (cp != NULL)
*cp = '\0'; // get rid of trailing \n
newDictionaryWordNode->word = strdup(wordFromFile);
if (*listhead == NULL) {
lastWordStored = *listhead = newDictionaryWordNode;
} else {
lastWordStored = lastWordStored->next = newDictionaryWordNode;
}
}
fclose(dictionaryFile);
return SUCCESS;
}