Creating a line counter using a Binary Search Tree **Specific Issues** - c

I want to create a C file which, via a binary-search-tree, reads from a text file, takes its identifiers (eg.include, stdio, hello, etc.), sorts them alphabetically, and returns what line number they appear on.
Desired terminal output:
Identifier: Hello
Counter: 2
Lines Appeared: [4, 6]
Issues:
q1.c:28:21: error: expected expression before '{' token
28 | n -> lineList = {};
When calling insert(), for the current Node, I want to add the current lineNumber to the tree->lineList[tree->counter-1].
test.txt
#include <stdio.h>
main() {
printf("Hello world!\n");
printf("Lochlann\n");
printf("Hello world!\n");
}
quetion.c
#include <stdio.h>
#include <ctype.h>
struct Node {
char * data;
int lineList[100];
int counter;
struct Word * word;
struct Node * ltree;
struct Node * rtree;
};
struct Node * head;
struct Node * newTree(char * identifier) {
struct Node * n = malloc(sizeof(struct Node));
n -> data = malloc(strlen(identifier) + 1);
n -> lineList = {};
n -> counter = 1;
strcpy(n -> data, identifier);
n -> ltree = n -> rtree = NULL;
return n;
}
struct Node * insert(struct Node * tree, char * identifier, int lineNumber) {
if (tree == NULL)
return newTree(identifier);
int cmp = strcmp(identifier, tree -> data);
if (cmp == 0) {
tree -> counter++;
tree -> lineList[tree -> counter - 1] = lineNumber;
return tree;
}
if (cmp < 0)
tree -> ltree = insert(tree -> ltree, identifier, lineNumber);
else
tree -> rtree = insert(tree -> rtree, identifier, lineNumber);
return tree;
}
void inorder(struct Node * tree) {
if (tree == NULL)
return;
inorder(tree -> ltree);
printf("Identifier: %s\nCounter: %i\nLines Appeared: ", tree -> data, tree -> counter);
for (int i = 0; i < tree -> counter; i++) {
printf("%d", tree -> lineList[i]);
//tree -> lineList[i] = lineNumber;
}
printf("\n");
inorder(tree -> rtree);
}
main() {
FILE * fp;
fp = fopen("test.txt", "r");
char buf[200];
char id[100];
int lineNumber = 1; //the tree->lineList should be [1,1,1,1,1,1]
int j;
while (fgets(buf, 100, fp)) {
int i = 0;
int len = strlen(buf);
for (j = 0, i = 0; i < len; i++) {
if (isalpha(buf[i]) || buf[i] == '_') {
while (buf[i] && (isalnum(buf[i]) || buf[i] == '_'))
id[j++] = buf[i++];
// have id
id[j] = 0;
//third argument adding line to linelist
head = insert(head, id, lineNumber);
j = 0;
}
}
}
inorder(head);
}

q1.c:28:21: error: expected expression before '{' token 28 | n -> lineList = {};
The problematic code is cited right there in the error message. The immediate issue is that the line is syntactically incorrect, because {} does not represent an assignable value of any type. But underneath that, you have a deeper problem: C arrays are not assignable in the first place. You can assign to array elements, but not to whole arrays.
There is a variety of functions you could use, such as memset() or memcpy(), but in this case, your best bet might be to perform the allocation of n with calloc() instead of malloc(). One of the distinctions between these is that calloc() initializes the allocated memory to all-bytes-zero.
When calling insert(), for the current Node, I want to add the current lineNumber to the tree->lineList[tree->counter-1].
Considering that C array indexes start at 0, not 1, I guess you want to do that before incrementing the node's counter. You are instead doing it after, when counter has a value one larger. Personally, though, I would start the counter for each node at 0 instead of at 1, and then do this on insertion:
tree->lineList[tree->counter++] = lineNumber;
That uses the value of the counter as of entry to the function as the index into lineList, and also increments the counter so that the next line number will go into the next position.

With little else on the go, here's a "mark-up" of your code. Please consider some / most / all of these comments for this program and for others.
#include <stdio.h>
#include <ctype.h>
struct Node {
char * data; // "generic" name. "token" might be better.
int lineList[100]; // limited, but okay for now
int counter;
// struct Word * word; // unused. keep things clean.
struct Node * ltree;
struct Node * rtree;
};
struct Node * head; // global variables are not recommended
struct Node * newTree(char * identifier, int lnNum ) {
// struct Node * n = malloc(sizeof(struct Node));
struct Node * n = calloc( 1, sizeof *n ); // use calloc
/* omitting test for NULL */
n -> data = malloc(strlen(identifier) + 1);
/* omitting test for NULL */
strcpy(n -> data, identifier); // do this here
// n -> lineList = {}; // unnecessary with calloc
n->lineList[ n->counter++ ] = lnNum; // missing!!!
// with calloc(), counter starts at zero
// strcpy(n -> data, identifier); // shifted to where used
// n -> ltree = n -> rtree = NULL; // unnecessary with calloc
return n;
}
struct Node * insert(struct Node * tree, char * identifier, int lineNumber) {
if (tree == NULL)
return newTree(identifier, lineNumber ); // missing param linenumber!
int cmp = strcmp(identifier, tree -> data);
if (cmp == 0) {
// tree -> counter++;
// tree -> lineList[tree -> counter - 1] = lineNumber;
/* omitting test for overrun */
tree -> lineList[ tree->counter ] = lineNumber;
tree->counter++;
return tree;
}
if (cmp < 0)
tree -> ltree = insert(tree -> ltree, identifier, lineNumber);
else
tree -> rtree = insert(tree -> rtree, identifier, lineNumber);
return tree;
}
void inorder(struct Node * tree) {
if (tree == NULL)
return;
inorder(tree -> ltree);
printf("Identifier: %s\nCounter: %i\nLines Appeared: ", tree -> data, tree -> counter);
for (int i = 0; i < tree -> counter; i++) {
printf("%d", tree -> lineList[i]);
//tree -> lineList[i] = lineNumber;
}
printf("\n");
inorder(tree -> rtree);
}
int main() { // proper declaration of 'main()'
FILE *fp = fopen("test.txt", "r"); // fewer lines
/* omitting test for success */
char buf[200];
// char id[100]; // not needed yet
int lineNumber = 0; // got zero lines so far
// int j; // willy-nilly variables
while (fgets(buf, sizeof buf, fp)) { // compiler knows dimensions
lineNumber++; // missing!
int len = strlen(buf);
for (int i = 0; i < len; /* i++ */) { // let body decide
if (isalpha(buf[i]) || buf[i] == '_') {
int j = 0; // NOW we need 'j' and..
char id[100];
while (buf[i] && (isalnum(buf[i]) || buf[i] == '_'))
id[j++] = buf[i++]; // here, 'i' is left on next character
// have id
id[j] = '\0'; // same, but conventional
//third argument adding line to linelist
head = insert(head, id, lineNumber);
// j = 0; // don't care about j anymore
}
else i++;
}
}
inorder(head);
return 0; // optional with newer compilers
}
You could even consider 'ditching' the copying of tokens from one buffer to another. Here's a slightly tighter for() loop for main():
for( int i = 0; buf[ i ]; i++ )
if (isalpha(buf[i]) || buf[i] == '_') {
int tokStart = i;
while (buf[i] && (isalnum(buf[i]) || buf[i] == '_'))
i++; // find end of token (or line)
char c = buf[i]; // remember this character
buf[i] = '\0'; // clobber it
head = insert(head, buf + tokStart, lineNumber);
buf[i--] = c; // restore character AND decrement
}

Related

Problems regarding an multiple choice question program

I have created a program to generate the result of a multiple choice exam. The program was supposed to show the total number of mistakes, blank answers and the number of the question which were answered incorrectly. For the following input:
6
1..223
(Here . means blank answer)
123124
The output was supposed to be:
Your result:
Mistakes: 3
Blanks: 2
Your mistakes are following:
4 5 6
Your blanks are following:
2 3
But the code shows undefined behavior. It seems to go through infinite loop. Expecting solution to my problem shortly. Thanks in advance.
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
char data;
struct node* next;
}node;
void printNode(node* head)
{
node* local = head;
int i = 0;
if(local -> data == 0)
{
printf("0");
return;
}
while(local != NULL)
{
if(i == 3)
{
i = 0;
printf("\n");
}
printf("%d\t", local -> data);
local = local -> next;
++i;
}
}
void freeNode(node** head)
{
node* temp = (*head);
while((*head) != NULL)
{
(*head) = (*head) -> next;
free(temp);
temp = (*head);
}
}
int main()
{
int n, i, flagB, flagM, blnk, mstk;
blnk = mstk = flagB = flagM = 0;
printf("Enter the number of questions: ");
scanf("%d", &n);
char ques[n], ans[n];
if(n == 0)
return 0;
node* headM = (node*)malloc(sizeof(node));
node* nodeM;
node* headB = (node*)malloc(sizeof(node));
node* nodeB;
printf("Enter your given answers: ");
fflush(stdin);
for(i = 0; i < n; ++i)
{
scanf("%c", &ques[i]);
}
fflush(stdin);
ques[n] = '\0';
printf("Enter the solution: ");
for(i = 0; i < n; ++i)
{
scanf("%c", &ans[i]);
}
ans[n] = '\0';
for(i = 0; i < n; ++i)
{
if(ques[i] == '.')
{
++blnk;
if(flagB == 0)
{
headB -> data = i + 1;
headB -> next = NULL;
nodeB = headB;
continue;
}
nodeB -> next = (node*)malloc(sizeof(node));
nodeB = nodeB -> next;
nodeB -> data = i + 1;
nodeB-> next = NULL;
flagB = 1;
}
else if(ques[i] != ans[i])
{
++mstk;
if(flagM == 0)
{
headM -> data = i + 1;
headM -> next = NULL;
nodeM = headM;
continue;
}
nodeM -> next = (node*)malloc(sizeof(node));
nodeM = nodeM -> next;
nodeM -> data = i;
nodeM-> next = NULL;
flagM = 1;
}
}
printf("Your result:\n\tMistakes: %d\n\tBlanks: %d\n", mstk, blnk);
printf("Your mistakes are follwing:\n");
printNode(headM);
printf("\nYour blanks are follwing:\n");
printNode(headB);
freeNode(&headM);
freeNode(&headM);
return 0;
}
Here are some additional thoughts. What makes your code very convoluted and hard to debug and keep the logic straight is you are mixing your linked-list Add function within the logic of your blanks and mistakes and using special conditions to handle adding the first node and subsequent nodes. This make things difficult to test and debug. If you need to add nodes to a linked-list, then write an add() function that you can thoroughly test and debug before putting it to use in your code.
Your VLAs ques and ans are too short to hold a string of n characters, at minimum they must be n + 1 characters long to provide storage for the nul-termining character that marks the end of the string. Ideally, you will make them at least 2-character longer to also hold the '\n' which will allow you to take input with fgets() rather than looping scanf() a character at a time -- which is just nuts.
You do not need to pass the address of the pointer to freeNode() simply pass a pointer. Sure freeNode() will receive a copy of the pointer -- but it will contain the original address -- and since you don't have to make any changes to that pointer available back to the caller, there is no need to pass the address of the pointer (there won't be any list left to worry about when you are done...)
So putting those pieces together, adding an add() function to add to your linked lists (See Linus on Understanding Pointers for why a pointer-to-pointer is used to iterate to the end), and adding a simple empty_stdin() function to remove the '\n' left in stdin from reading n with scanf() before making calls to fgets() later for ques and ans, you could do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* simple function to empty stdin to end-of-line */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
typedef struct node
{
int data;
struct node *next;
} node;
node *add(node **head, int v)
{
node **ppn = head, /* pointer to pointer to head */
*pn = *head, /* pointer to head */
*newn = malloc (sizeof *newn); /* allocate new node */
if (!newn) { /* validate allocation */
perror ("malloc-node");
return NULL;
}
newn->data = v; /* initialize members values */
newn->next = NULL;
while (pn) { /* iterate to end of list */
ppn = &pn->next;
pn = pn->next;
}
return *ppn = newn; /* add & return new node */
}
void printNode (node *head)
{
for (; head; head = head->next)
printf (" %d", head->data);
putchar ('\n');
}
void freeNode(node *head)
{
while (head != NULL)
{
node *victim = head;
head = head->next;
free(victim);
}
}
int main()
{
int n, i, blnk, mstk;
blnk = mstk = 0;
node *headM = NULL; /* declare pointers and initialize NULL */
node *headB = NULL;
printf ("Enter the number of questions: ");
/* you must VALIDATE every user-input */
if (scanf ("%d", &n) != 1) {
fputs ("error: invalid integer input.\n", stderr);
return 1;
}
empty_stdin(); /* remove '\n' (and any other chars from user) */
/* before calling fgets() below */
if (n == 0) /* check 0 BEFORE VLA declaration */
return 0;
char ques[2*n], ans[2*n]; /* declare question/answer VLAs, don't skimp */
printf("Enter your given answers: ");
if (!fgets(ques, sizeof ques, stdin)) /* read ques from stdin */
return 1;
ques[strcspn(ques, "\r\n")] = 0; /* trim '\n' from end of ques */
printf("Enter the solution: ");
if (!fgets(ans, sizeof ans, stdin)) /* read ans from stdin */
return 1;
ans[strcspn(ans, "\r\n")] = 0; /* ditto for ans */
for(i = 0; i < n; ++i) /* loop n times */
{
if(ques[i] == '.') /* if blank */
{
add (&headB, i + 1); /* add to list headB */
++blnk; /* increment counter */
}
else if(ques[i] != ans[i]) /* if mistake */
{
add (&headM, i + 1); /* add to list headM */
++mstk; /* increment counter */
}
}
printf ("Your result:\n\tMistakes: %d\n\tBlanks: %d\n"
"Your mistakes are following:\n", mstk, blnk);
printNode(headM);
printf("\nYour blanks are following:\n");
printNode(headB);
freeNode(headM); /* no need to pass the address of the pointer to free */
freeNode(headB); /* there won't be a list left when freeNode is done */
return 0;
}
There is a lot there, so go through it slowly.
Example Use/Output
$ ./bin/llquestions
Enter the number of questions: 6
Enter your given answers: 1..223
Enter the solution: 123124
Your result:
Mistakes: 2
Blanks: 2
Your mistakes are following:
4 6
Your blanks are following:
2 3
(note: in 1..223 and 123124, 5 is not a mistake, the 2 is in the correct position at the end)
Look things over and let me know if you have further questions.
I made some changes to this code, check this out.
#include <stdio.h>
#include <stdlib.h>
typedef struct Node node;
struct Node
{
int data;
struct Node * next;
};
void printNode(node *head)
{
node *local = head;
while (local != NULL)
{
printf("%d ", local->data);
local = local->next;
}
}
void freeNode(node **head)
{
node *temp = (*head);
while ((*head) != NULL)
{
(*head) = (*head)->next;
free(temp);
temp = (*head);
}
}
int main()
{
int n, i, flagB = 0, flagM = 0, blnk = 0, mstk = 0;
blnk = mstk = flagB = flagM = 0;
printf("Enter the number of questions: ");
scanf("%d", &n);
char ques[n], ans[n];
if (n == 0)
return 0;
node *headM = (node*) malloc(sizeof(node));
headM->data = 0;
node *nodeM = headM;
node *headB = (node*) malloc(sizeof(node));
headB->next = 0;
node *nodeB = headB;
printf("Enter your given answers: ");
for (i = 0; i < n; ++i)
{
scanf("%s", &ques[i]);
}
ques[n] = '\0';
fflush(stdin);
printf("Enter the solution: ");
for (i = 0; i < n; ++i)
{
scanf("%s", &ans[i]);
}
ans[n] = '\0';
fflush(stdin);
for (i = 0; i < n; ++i)
{
if (ques[i] == '.')
{ ++blnk;
if (flagB == 0)
{
nodeB->data = i + 1;
nodeB->next = NULL;
flagB = 1;
continue;
}
nodeB->next = (node*) malloc(sizeof(node));
nodeB = nodeB->next;
nodeB->data = i + 1;
nodeB->next = NULL;
}
else if (ques[i] != ans[i])
{ ++mstk;
if (flagM == 0)
{
nodeM->data = i + 1;
nodeM->next = NULL;
flagM = 1;
continue;
}
nodeM->next = (node*) malloc(sizeof(node));
nodeM = nodeM->next;
nodeM->data = i + 1;
nodeM->next = NULL;
//flagM = 1; //You made a mistake here
}
}
nodeM = headM;
nodeB = headB;
printf("Your result:\n\tMistakes: %d\n\tBlanks: %d\n", mstk, blnk);
printf("Your mistakes are following question numbers:\n");
if (mstk != 0)
printNode(headM);
else
printf("No Mistakes\n");
printf("\nYour blanks are following question numbers:\n");
if (blnk != 0)
printNode(headB);
else
printf("No Blanks\n");
freeNode(&headM);
freeNode(&headM);
return 0;
}

segmentation fault in a linked list while loop?

I'm trying to setup a graph in C. I tried the graph with user input and it works perfectly. However, i am trying to implement a read from file. The last else statement is where the error is coming from because when i commented it out it compiles without any problems. I have included a comment over the block i think that has the problem. Please let me know if there is anything else needed for this question.
#include <stdio.h>
#include <stdlib.h>
struct node{
int data;
struct node* next;
};
//int counter and mainVertex would be used to determine if graph is connected.
// void graphConnection(){
//
//
//
//
//
//
// }
char* deblank(char* input)
{
int i,j;
char *output=input;
for (i = 0, j = 0; i<strlen(input); i++,j++)
{
if (input[i]!=' ')
output[j]=input[i];
else
j--;
}
output[j]=0;
return output;
}
struct node *G[1000];
int counter = 0;
char *mainVertex;
void readingEachLine(){
FILE * fp;
char * line = NULL;
size_t len = 0;
ssize_t read;
//Read file and exit if fail
fp = fopen("test.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
while ((read = getline(&line, &len, fp)) != -1) {
line = deblank(line);
int i = 0;
struct node* cursor = malloc(sizeof(struct node));
struct node* secondcursor = malloc(sizeof(struct node));
struct node* tempitem;
while(line[i] != '\n'){
//If its the first of the line look into the array and set struct cursor to the corresponding
//array position
if (i == 0){
mainVertex[counter] = line[0];
int convertor = line[i] - '0';
cursor = G[convertor];
counter++;
}
//if its not the first, then set a struct with that number as data
else{
tempitem = malloc(sizeof(struct node));
int convertor = line[i] - '0';
tempitem->data = convertor;
tempitem->next = NULL;
}
//if there is no element connected to the struct in array, connect the tempitem
if (cursor->next == NULL){
cursor->next = tempitem;
}
//If there are already connected elements, loop until the end of the linked list
//and append the tempitem
//ERROR: I GET SEGMENTATION FAULT FROM HERE. TRIED AFTER COMMENTING IT OUT
else{
secondcursor = cursor;
while(secondcursor->next != NULL){
secondcursor = secondcursor->next;
}
secondcursor->next = tempitem;
}
i++;
}
printf("\n");
}
}
int main(void){
for (int i = 1; i < 1000; i++)
{
G[i]= malloc(sizeof(struct node));
G[i]->data = i;
G[i]->next = NULL;
}
readingEachLine();
}
EDIT: This is how the text file looks like:
1 3 4
2 4
3 1 4
4 2 1 3
Your code has several misconceoptions:
Apparently, you can have a maximum of 1,000 nodes. You have an array G of 1,000 head pointers to linked lists. Don't allocate memory for all 1,000 nodes at the beginning. At the beginning, all lists are empty and an empty linked list is one that has no node and whose head is NULL.
In your example, cursor is used to iterate oer already existing pointers, so don't allocate memory for it. If you have code like this:
struct node *p = malloc(...);
// next use of p:
p = other_node;
you shouldn't allocate. You would overwrite p and lose the handle to the allocated memory. Not all pointers have to be initialised with malloc; allocate only if you create a node.
Your idea to strip all spaces from a line and then parse single digits will fail if you ever have more then 9 nodes. (But you cater for 1,000 node.) Don't try to parse the numbers yourself. There are library functions for that, for example strtol.
It is not clear what mainVertex is supposed to be. You use it only once, when you assign to it. You treat it like an array, but it is a global pointer, initialised to NULL. When you dereference it, you get undefined behaviour, which is where your segmentation fault probably comes from.
Here's a program that does what you want to do. (It always inserts nodes at the head for simplicity and it should have more allocation checks.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum {
maxNodes = 1000
};
struct node{
int data;
struct node* next;
};
struct node *G[maxNodes];
size_t nnode = 0;
int read_graph(const char *fn)
{
FILE * fp;
char * line = NULL;
size_t len = 0;
fp = fopen(fn, "r");
if (fp == NULL) return -1;
while (getline(&line, &len, fp) != -1) {
char *p;
char *end;
int id;
int n;
id = strtol(line, &end, 10);
if (end == line) continue;
if (id < 1 || id > maxNodes) break;
if (id > nnode) nnode = id;
id--;
p = end;
n = strtol(p, &end, 10);
while (p != end) {
struct node *nnew = malloc(sizeof(*nnew));
nnew->data = n - 1;
nnew->next = G[id];
G[id] = nnew;
p = end;
n = strtol(p, &end, 10);
}
}
fclose(fp);
free(line);
return 0;
}
int main(void)
{
if (read_graph("test.txt") < 0) {
fprintf(stderr, "Couldn't gread raph.\n");
exit(1);
}
for (int i = 0; i < nnode; i++) {
struct node *p = G[i];
if (p) {
printf("%d:", i + 1);
for (; p; p = p->next) {
printf(" %d", p->data + 1);
}
puts("");
}
}
for (int i = 0; i < nnode; i++) {
struct node *p = G[i];
while (p) {
struct node *old = p;
p = p->next;
free(old);
}
}
return 0;
}

Reading file into linked list

I am trying to read a text file I made into a linked list, the text file looks like this:
around 1 2 1
bread 2 4 3 5 1
four 1 3 2
head 3 1 2 2 1 5 1
has 2 3 1 5 2
Where the first string of each line are just words from a paragraph. The first number after the word is the number of lines the word was found in, in the paragraph. Then the following numbers are pairs of (line, occurrences) in the paragraph.
For example, for the word bread:
It was found in 2 lines in the paragraph. In the first line, line 4, it was found 3 times. Then in the second line, line 5, it was found 1 time.
I am trying to create a linked list from this text file, my program looks like this so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#define MAXWORD 999
typedef struct node node_t;
struct node {
char *word;
int num_lines;
int paragraph;
int freq;
node_t *next;
};
int
main(int argc, char *argv[]) {
FILE *fp;
char word[MAXWORD+1];
int ch, line_count = 0, len = 0;
node_t *node = (node_t*)malloc(sizeof(*node));
node_t *curr, *prev;
fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "Error reading file\n");
exit(EXIT_FAILURE);
}
/* Just trying to store the string so far */
while ((ch = getc(fp)) != EOF) {
if (ch == '\n') {
line_count++;
strcpy(node->word, word);
}
if (isalpha(ch)) {
word[len] = ch;
len++;
word[len] = '\0';
}
if (isdigit(ch)) {
len = 0;
}
}
printf("line count = %d", line_count);
free(node)
fclose(fp);
return 0;
}
In this snippet, I have been trying to store the string in the linked list data structure, but I have not yet used dynamic arrays to store the numbers after the word which occur in the text file. I know I will need to build this data structure using malloc() and realloc(), but I am unsure of how to do this.
How should I do this?
My desired output would look like this:
There are five words in the text file,
and 9 pairs of (line, occurences)
Word: pairs
"around": 2,1
"bread": 4,3; 5,1
"four": 3,2
"head": 1,2; 2,1; 5,1
"has": 3,1; 5,2
UPDATE
I have been researching this and it seems to be very similar to the inverted index problem, where I have seen that using a binary search tree would be best.
Could I implement my binary search tree like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#define MAXWORD 999
typedef char word_t[MAXWORD+1];
typedef struct node node_t;
struct node {
void *data;
int *ints;
node_t *rght;
node_t *left;
};
typedef struct {
node_t *root;
int (*cmp)(void*, void*);
} tree_t;
int
main(int argc, char *argv[]) {
FILE *fp;
fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "Error reading file\n");
exit(EXIT_FAILURE);
}
while ((ch = getc(fp)) != EOF) {
if (ch == '\n') {
line_count++;
}
}
fclose(fp);
return 0;
}
You could do something like this:
typedef struct {
int paragraph;
int freq;
} stats_t;
struct node {
char *word;
int num_lines;
stats_t *stats;
node_t *next;
};
Then after you parse the string you can do:
ps = calloc(line_count, sizeof(stats_t));
to get a pointer to an array of stats_t structs, which you can fill with line locations and frequencies. Then you can store the pointer ps in your node struct.
I wrote a program that does what I think you are looking for. I modified the structs I was thinking about before:
typedef node node_t;
struct node {
char *word;
int num_lines;
int *location;
int *frequency;
node_t *next;
};
This way the nodes contain pointers to arrays of int to store the location and frequency information. Nodes and storage for the word strings, location arrays, and frequency arrays are all dynamically allocated. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAXLINE 1000
#define MAXWORD 30
typedef struct node node_t;
struct node {
char *word;
int num_lines;
int *location;
int *frequency;
node_t *next;
};
void strip(char *pln);
void normalize_word(char *pstr);
struct node * update_word(char *pwd, int lnum, struct node *phead);
struct node * find_in_list(char *pwd, struct node *phead);
int find_line_pair(int lnum, struct node *pwn);
int list_len(struct node *phead);
int num_pairs(struct node *phead);
int main(int argc, char *argv[])
{
FILE *fp;
struct node *head, *current;
char *pline, *pword;
char line[MAXLINE + 1];
char word[MAXWORD + 1];
int i, n, line_count = 0;
head = NULL;
if (argc < 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
} else {
if ((fp = fopen(argv[1], "r")) == NULL) {
fprintf(stderr, "Unable to open file %s\n", argv[1]);
exit(EXIT_FAILURE);
}
}
/* Read in lines and process words */
pline = line;
pword = word;
while (fgets(pline, MAXLINE, fp) != NULL) {
++line_count;
strip(pline);
while ((pword = strtok(pline, " ")) != NULL) {
normalize_word(pword);
if (*pword != '\0') // don't add empty words
head = update_word(pword, line_count, head);
pline = NULL;
}
pline = line;
}
/* Display list contents */
printf("There are %d words in the text file,\n",
list_len(head));
printf("and %d pairs of (line, occurrences)\n",
num_pairs(head));
printf("Word: pairs\n");
current = head;
while (current != NULL) {
n = current->num_lines;
printf("%s:", current->word);
for (i = 0; i < n; i++) {
printf(" %d, %d;",
current->location[i], current->frequency[i]);
}
putchar('\n');
current = current->next;
}
/* Cleanup */
// close file
if (fclose(fp) != 0)
fprintf(stderr, "Error closing file %s\n", argv[1]);
// free all allocated memory
current = head;
while (current != NULL) {
free(current->word);
free(current->location);
free(current->frequency);
current = current->next;
free(head);
head = current;
}
return 0;
}
/* Remove trailing newlines */
void strip(char *pln)
{
while (*pln != '\0') {
if (*pln == '\n')
*pln = '\0';
++pln;
}
}
/* Convert word to lowercase and remove trailing
* non-alphanumeric characters */
void normalize_word(char *pstr)
{
int i = 0;
char ch;
while ((ch = pstr[i]) != '\0') {
pstr[i] = tolower(ch);
++i;
}
while ((--i >= 0) && !isalnum(pstr[i])) {
pstr[i] = '\0';
continue;
}
}
/* Update existing word node or create a new one, and return
* a pointer to the head of the list */
struct node * update_word(char *pwd, int lnum, struct node *phead)
{
struct node *found, *newnode;
char *pword;
int *ploc, *pfreq;
int index;
/* Modify existing node if word is in list */
if ((found = find_in_list(pwd, phead)) != NULL) {
// add new (location, freq) pair if word not in found line
if ((index = find_line_pair(lnum, found)) == -1) {
index = found->num_lines; // index for new pair
found->num_lines += 1; // increment number of lines
ploc = realloc(found->location, (index + 1) * sizeof(int));
pfreq = realloc(found->frequency, (index + 1) * sizeof(int));
ploc[index] = lnum; // new location
pfreq[index] = 1; // found once in this line so far
found->location = ploc; // point to new location array
found->frequency = pfreq; // point to new frequency array
}
else { // update frequency in existing line
found->frequency[index] += 1;
}
/* Set up a new node */
} else {
// allocate memory for new node
newnode = malloc(sizeof(struct node));
// allocate memory for string pointed to from node
pword = malloc((strlen (pwd) + 1) * sizeof(char));
strcpy(pword, pwd);
newnode->word = pword; // set word pointer
newnode->num_lines = 1; // only one line so far
ploc = malloc(sizeof(int));
pfreq = malloc(sizeof(int));
*ploc = lnum; // location was passed by caller
*pfreq = 1; // only one occurrence so far
newnode->location = ploc;
newnode->frequency = pfreq;
if (phead == NULL) { // if wordlist is empty
newnode->next = NULL; // only/last link in the list
phead = newnode; // newnode is the head
} else {
newnode->next = phead; // insert newnode at front of list
phead = newnode;
}
}
return phead;
}
/* Return pointer to node containing word, or NULL */
struct node * find_in_list(char *pwd, struct node *phead)
{
struct node *current = phead;
while (current != NULL) {
if (strcmp(current->word, pwd) == 0)
return current; // word already in list
current = current->next;
}
return NULL; // word not found
}
/* Return index of existing line location, or -1 */
int find_line_pair(int lnum, struct node *pwn)
{
int n = pwn->num_lines;
int index = 0;
while (index < n) {
if (pwn->location[index] == lnum)
return index; // word already found in this line
++index;
}
return -1; // word not yet found in this line
}
/* Find number of nodes in linked list */
int list_len(struct node *phead)
{
int length = 0;
struct node *current = phead;
while (current != NULL) {
++length;
current = current->next;
}
return length;
}
/* Find number of (line, occurrence) pairs */
int num_pairs(struct node *phead)
{
int num = 0;
struct node *current = phead;
while (current != NULL) {
num += current->num_lines;
current = current->next;
}
return num;
}
Note: I modified this from the previous version in the update_word() function. The original code inserted a new node at the end of the list, so the resulting list contained words in order of their first appearance in the input text. This version inserts a new node at the beginning of the list, so the resulting list contains words in reverse order of their first appearance. This speeds up node insertion and simplifies the node-insertion code from:
current = phead;
while (current->next != NULL) // find tail
current = current->next;
current->next = newnode; // add newnode to end
to:
newnode->next = phead; // insert newnode at front of list
I have no doubt that the code can be improved, but this does seem to work. I wouldn't say that this is exactly simple, but relatively straightforward. I ran it against this text file:
Three blind mice. Three blind mice.
See how they run. See how they run.
They all ran after the farmer's wife,
Who cut off their tails with a carving knife,
Did you ever see such a sight in your life,
As three blind mice?
Here are the results:
There are 31 words in the text file,
and 37 pairs of (line, occurrences)
Word: pairs
as: 6, 1;
life: 5, 1;
your: 5, 1;
in: 5, 1;
sight: 5, 1;
such: 5, 1;
ever: 5, 1;
you: 5, 1;
did: 5, 1;
knife: 4, 1;
carving: 4, 1;
a: 4, 1; 5, 1;
with: 4, 1;
tails: 4, 1;
their: 4, 1;
off: 4, 1;
cut: 4, 1;
who: 4, 1;
wife: 3, 1;
farmer's: 3, 1;
the: 3, 1;
after: 3, 1;
ran: 3, 1;
all: 3, 1;
run: 2, 2;
they: 2, 2; 3, 1;
how: 2, 2;
see: 2, 2; 5, 1;
mice: 1, 2; 6, 1;
blind: 1, 2; 6, 1;
three: 1, 2; 6, 1;
Here is my version using Binary Search Tree (BST):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef struct internal_node in_node;
struct internal_node{
int line;
int freq;
in_node* next;
};
struct tree{
char *word;
int num_lines;
in_node* in_nodeptr;
in_node* current;
struct tree* right;
struct tree* left;
};
typedef struct tree* treeptr;
void free_list(in_node* in_nodeptr){
if(in_nodeptr!=NULL) {
free(in_nodeptr);
}
}
void free_bst(treeptr head){
if (head!=NULL) {
free_bst(head->right);
free_bst(head->left);
free_list(head->in_nodeptr);
free(head->word);
free(head);
}
}
void print_list(in_node* in_nodeptr){
while(in_nodeptr!=NULL){
printf("%d %d; ",in_nodeptr->line,in_nodeptr->freq);
in_nodeptr=in_nodeptr->next;
}
}
void print_bst(treeptr head){
if(head!=NULL){
printf("%s: ",head->word);
print_list(head->in_nodeptr);
printf("\n");
print_bst(head->right);
print_bst(head->left);
}
}
void input_to_bst(treeptr* head,char* word,int line){
if((*head)==NULL){
(*head)=(treeptr)malloc(sizeof(struct tree));
(*head)->word=(char*)malloc(50*sizeof(char));
strcpy(((*head)->word),word);
(*head)->num_lines=1;
(*head)->right=NULL;
(*head)->left=NULL;
(*head)->in_nodeptr=(in_node*)malloc(sizeof(in_node));
(*head)->in_nodeptr->line=line;
(*head)->in_nodeptr->freq=1;
(*head)->in_nodeptr->next=NULL;
(*head)->current=(*head)->in_nodeptr;
}
else{
int check=strcmp(((*head)->word),word);
if(check>0) input_to_bst(&((*head)->left),word,line);
else if(check<0) input_to_bst(&((*head)->right),word,line);
else{
if( (*head)->current->line==line) (*head)->current->freq++;
else {
(*head)->current->next=(in_node*)malloc(sizeof(in_node));
(*head)->current->next->line=line;
(*head)->current->next->freq=1;
(*head)->current->next->next=NULL;
}
}
}
}
int main(int argc, char *argv[]) {
treeptr head=NULL;
FILE *fp=fopen(argv[1], "r");
char word[50],ch;
int len=0,lines=1;
if (fp == NULL) {
fprintf(stderr, "Error reading file\n");
exit(1);
}
while ((ch = getc(fp)) != EOF) {
if (ch == '\n') {
word[len]='\0';
if(len>0) input_to_bst(&head,word,lines);
len=0;
lines++;
}
else if (ch==' '){
word[len]='\0';
if(len>0) input_to_bst(&head,word,lines);
len=0;
}
else if (isalpha(ch)){
word[len]=ch;
len++;
}
}
if(len>0) {
word[len]='\0';
input_to_bst(&head,word,lines);
}
print_bst(head);
fclose(fp);
free_bst(head);
return 0;
}
Every word is held as a node of the BST and also each node of BST except from the word, holds a list with all the appearances (lines and frequency ) of the word. In order to be as most efficient as possible we hold a pointer (in_node* current) to the last element of list of appearance so that we don't need to traverses every time we need to add an appearance.
As an example:
Text:
C is an imperative procedural language. It was designed to be compiled
using a relatively straightforward compiler and to require minimal
runtime support.
Output:
C: 1 1;
is: 1 1;
procedural: 1 1;
was: 1 1;
to: 1 1; 2 1;
using: 2 1;
relatively: 2 1;
straightforward: 2 1;
support: 3 1;
require: 2 1;
runtime: 3 1;
language: 1 1;
minimal: 2 1;
an: 1 1;
imperative: 1 1;
designed: 1 1;
be: 1 1;
compiled: 1 1;
compiler: 2 1;
and: 2 1;
It: 1 1;
a: 2 1;
Note that the above implementation is case sensitive for example "And" is different from "and".
If you don't wish to be case sensitive just replace the line word[len]=ch; with word[len]=tolower(ch); and works fine.
The complexity of the above algorithm is O(n^2) which would be the same if you used only linked lists but in the average case BST is O(nlogn) which is much better than linked lists and this is the reason that it is considered to be the better.
Also note that since we must keep a list for appearances of each word the complexity would be worst if we didn't keep the in_node* current pointer which gives us access to the end of each appearance list in constant time (O(1)). So I think that as terms of complexity you can't go better than O(nlogn).

Linked lists, operations with parameter

I'm trying to implement program in with i can create ~arbitrary number of singly linked lists dynamically and perform operations on particular one (defined by parameter). I create dynamic array of head pointers so that i can refer to the certain head node defined by paramater(index of an array + 1). Parameter is just (1,2,3..number of lists). So far I have managed to implement only initialise and push function but the program after complilation doesn't work as expected. Where is the problem?
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define CHUNK 10
typedef struct
{
char *str;
struct node *next;
} node;
node *initialise(node **array, int *amount_of_lists);
void push(node **array, int *amount_of_lists);
char *getString(void);
int main()
{
node **heads = NULL; //initially null, pointer to the dynamic array of head pointers
int amount_of_lists = 0;
int *no_of_heads = &amount_of_lists;
initialise(heads, no_of_heads);
initialise(heads, no_of_heads);
push(heads, no_of_heads);
push(heads, no_of_heads);
return 0;
}
node *initialise( node **array, int *amount_of_lists ) /*reallocate memory for another head pointer ans return the pointer to node*/
{
++(*amount_of_lists);
printf("\n%d", *amount_of_lists);
array = (node**)realloc(array, sizeof(node*)*(*amount_of_lists));
return array[(*amount_of_lists) - 1] = malloc(sizeof(node));
}
int readParameter(int *amount_of_lists)
{
int parameter = 0, control = 0;
bool repeat = 0;
do
{
if(repeat)
{
printf("\nWrong parameter, try again.");
}
printf("\n Enter list parameter: ");
control = scanf("%d", &parameter);
fflush(stdin);
repeat = 1;
}
while( control != 1 || parameter < 1 || parameter > (*amount_of_lists) );
return parameter;
}
void push(node **array, int *amount_of_lists)
{
int parameter = readParameter(amount_of_lists) - 1;
node *temp = array[parameter];
array[parameter] = malloc(sizeof(node));
array[parameter] -> next = temp;
array[parameter] -> str = getString();
}
char *getString(void)
{
char *line = NULL, *tmp = NULL;
size_t size = 0, index = 0;
int ch = EOF;
while (ch)
{
ch = getc(stdin);
/* Check if we need to stop. */
if (ch == EOF || ch == '\n')
ch = 0;
/* Check if we need to expand. */
if (size <= index)
{
size += CHUNK;
tmp = realloc(line, size);
if (!tmp)
{
free(line);
line = NULL;
break;
}
line = tmp;
}
/* Actually store the thing. */
line[index++] = ch;
}
return line;
}
As BLUEPIXY somewhat crypticly hinted at in his comment 1), in order to modify main()'s heads in initialise(), you have to pass heads by reference to initialise(), i. e. change
initialise(heads, no_of_heads);
initialise(heads, no_of_heads);
to
initialise(&heads, no_of_heads);
initialise(&heads, no_of_heads);
consequently
node *initialise( node **array, int *amount_of_lists )
changes to
node *initialise(node ***array, int *amount_of_lists)
and inside array changes to *array, i. e.
*array = realloc(*array, sizeof(node *) * *amount_of_lists);
return (*array)[*amount_of_lists - 1] = malloc(sizeof(node));

How to print a 2D array in C

I've been trying to get my program to print a barchart.
The issue is at the bottom, where I make a 2D array to hold the values and then attempt to
print the array. The problem is that is prints nothing. I've tried to solve it for a few hours with no luck. Any suggestions?
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#define DELIM " " /* the delimiter */
#define MAX_CHANGE (10.0/86400.0) /* 10kg/day */
/* seconds in a day is 24 hours * 60 minutes * 60 seconds */
/* return 0 if the passed strings don't math, 1 otherwise */
/* defines the structure of Node */
struct Node{
char * id;
float weight;
int time;
int count;
struct Node * next;
} *head, *p, *t, *last;
/* Constructor which returns a pointer to a new node*/
struct Node *newNode(int *time, char * id, float *w)
{ /*note malloc returns a pointer */
struct Node *r = (struct Node *)malloc( sizeof(struct Node) );
r->time = *time;
r->id = strdup(id); //a duplicate id is made to prevent all the nodes from using the same userID
r->weight = *w;
r->count = 1;
r->next = NULL;
return r;
}
/* prints the list starting with head */
printList(struct Node * head)
{
while(head != NULL)
{
printf("%d %s %f\n",head->time,head->id,head->weight);
head = head->next;
}
return 0;
}
int main() {
char line[1024];
int lasttime = 0;
int success;
int timestamp;
int duration;
char userID[1000] = "";
char *token;
char temp[1000];
float weight;
float lastweight;
float change;
float changePerTime;
head = (struct Node*)malloc(sizeof(struct Node));
head->id = "";
head->weight = 0.0;
head->time = 0;
head->next = NULL;
last = head;
/*FILE * f = fopen("C:\\Users\\Chris\\Documents\\School\\York\\Computer Science\\2031 Software Tools\\Labs\\lab3\\testcases\\01.in","r"); */
/* last points to the last node in the list
head is always the same node
p is used to travers the list
t is a pointer the most recent occurrense of a user record
*/
while (fgets(line,1024,stdin) != NULL) {
userID[0] ='\0'; // resets userID
token = strtok(line, DELIM);
success = sscanf(token,"%d",&timestamp);
if (success < 1 || timestamp == 0)
{
printf("Invalid time\n");
continue;
}
while((token = strtok(NULL,DELIM) ) != NULL && token[0] != '.' && ! isdigit(token[0]) )
{
strcpy(temp,token); //
strcat(temp,DELIM ); // adds space between each token
strcat(userID, temp); // src temp must be a const string, not a pointer
temp[0] = '\0';
}
userID[strlen(userID)-1] = '\0'; //erases the tailing space.
if(strlen(userID) > 179 || !strlen(userID) )
{printf("Illegal userID\n"); continue; }
else if(token == NULL || sscanf(token,"%f", &weight) < 1 || weight < 30.0 || weight > 300.0)
{printf("Illegal weight\n"); continue; }
else if (lasttime >= timestamp)
{printf("Nonmonotonic timestamps\n"); continue; }
else {
/* sets t to last found user record and sets "last" to the last record*/
for(p = head, t = NULL; p != NULL; p = p->next)
{
if(strcmp(userID,p->id) == 0)
{
t=p;
}
last = p; // set last to last p.
}
if(t == NULL)
{
printf("OK newuser\n");
}
else if(t != NULL)
{
/* increments count of id's for this user */
(t->count)++;
duration = timestamp - t->time;
change = weight - t->weight;
changePerTime = change / duration;
if(changePerTime < -MAX_CHANGE || changePerTime > MAX_CHANGE)
printf("Suspiciously large weight change\n");
else
printf("OK\n");
}
/* adds node to end of list */
last->next = newNode(&timestamp,userID,&weight);
last = last->next;
/* adds time to last time */
lasttime = timestamp;
}
}
//fclose(f);
char bc[10][last->count];
int j, i, k, bh;
for(p = head; p != NULL, j <= last->count; p=p->next)
{
if(strcmp(last->id,p->id) == 0)
{
for(i = 11, k=0, bh = (int)(p->weight / 30);i >= 0; i--)
{
if(k < bh)
{
bc[i][j] = '*';
k++;
}
else bc[i][j] = ' ';
}
j++;
}
}
//printf("%c", bc[9][1]);
int m=0, n=0;
for(m < 10; m++;)
{
for(n=0 ;n < last->count; n++)
{
printf("%c",bc[m][n]);
}
printf("%c",'\n');
}
}
Your outer for loop parts are incorrectly placed. Instead of:
for(m < 10; m++;)
You want:
for(m=0;m < 10; m++)
The condition, m<10, is the second part of the for loop, whereas you've mistakenly put it in the initialization part of the loop. Similarly, the increment statement, i++, was in your condition part, so you had no incrementing of the m variable happening.

Resources