Why is my dictionary not counting occurrences correctly? - c

I'm entering words into a binary tree and also counting the number of times they appear.
This is the text file: http://pastebin.com/FY9ZTQX6
Here is my code so far. It all happens in the insert() function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
/*
Name: Marcus Lorenzana
Assignment: Final
*/
/*binary tree struct to hold left and right node
as well as the word and number of occurrences*/
typedef struct node
{
char *word;
int count;
struct node *left;
struct node *right;
}
node;
//,.:;-
int punctuation[4] = {0};
/*Function Prototypes*/
void insert(node ** dictionary, node * entry);
char* readFile(char* filename);
void printDictionary(node * tree);
void printPunctuation();
void toLower(char** word);
char *replace(char *st, char *orig, char *repl);;
int main()
{
char *word;
char* filecontents = readFile("data.txt");
FILE *fp = fopen("data.txt","r");
//create dictionary node
node *dictionary;
node *entry;
//read words and punctuation in from the text file
word = strtok (filecontents, " \n");
dictionary = NULL;
//create and add lowercase entries into the dictionary
//by calling insert function
while (word != NULL)
{
//store punctuation in it's own array of occurrences
//since there are only 4 to watch out for
if (word[0]==',') {
punctuation[0]++;
word = strtok (NULL, " \n");
} else if (word[0]=='.') {
punctuation[1]++;
word = strtok (NULL, " \n");
}
else if (word[0]==';') {
punctuation[2]++;
word = strtok (NULL, " \n");
}
else if (word[0]=='-') {
punctuation[3]++;
word = strtok (NULL, " \n");
}
else if (word[0]=='$') {
break;
}
//use binary tree to store unique words
else {
//convert word to lowercase first, so capital words
//won't have precedence
word = strlwr(word);
//create entry node and call insert with dictionary being call by reference
//so that is is mutable
entry = (node *) malloc(sizeof(node));
entry->left = entry->right = NULL;
entry->word = malloc(sizeof(char)*(strlen(word)+1));
entry->word = word;
insert(&dictionary,entry);
word = strtok (NULL, " \n");
}
}
//print the dictionary of words and their number of occurrences
printDictionary(dictionary);
//print the punctuation and their number of occurrences
printPunctuation();
//find word to be replaced
int found = 0;
char buffer[250];
char src[50] , dest[50];
while(fgets(buffer , sizeof(buffer) , fp) != NULL)
{
if(strchr(buffer , '<'))
{
found++;
break;
}
}
if(found)
{
fscanf(fp , "%s < %s" , src , dest);
}
//example of replace()
FILE *fout = fopen("newdata.txt","w");
filecontents = readFile("data.txt");
fprintf(fout,"%s",replace(filecontents,src,dest));
fclose(fout);
fclose(fp);
return 0;
}
/*inserts an entry into the dictionary in lexicographical order*/
void insert(node ** dictionary, node * entry)
{
//if there are no entries, set dictionary point
//to new one and set count to 1
if(!(*dictionary))
{
*dictionary = entry;
(*dictionary)->count=1;
return;
}
//compare word to see if it of higher
//or lower alphabetical order
int result = strcmp(entry->word,(*dictionary)->word);
//if it is lower, place on the left
if(result<0)
{
insert(&(*dictionary)->left, entry);
(*dictionary)->count=1;
}
//if it is higher, place on the right
if(result>0)
{
insert(&(*dictionary)->right, entry);
(*dictionary)->count=1;
}
//if it is equal, don't create a new entry but update the count
if(result == 0)
{
(*dictionary)->count++;
}
}
/*put file contents in string for strtok*/
char* readFile(char* filename)
{
FILE* file = fopen(filename,"r");
if(file == NULL)
{
return NULL;
}
fseek(file, 0, SEEK_END);
long int size = ftell(file);
rewind(file);
char* content = calloc(size + 1, 1);
fread(content,1,size,file);
return content;
}
/*prints the dictionary in lexicographical order
and number of occurrences for each word*/
void printDictionary(node * dictionary)
{
//traverse dictionary in lexicographical order
if(dictionary->left)
{
printDictionary(dictionary->left);
}
//print word and number of occurrences
printf("%s\n",dictionary->word);
printf("=%d\n",dictionary->count);
if(dictionary->right)
{
printDictionary(dictionary->right);
}
}
/*prints the punctuation and number of occurrences*/
void printPunctuation(){
//,.:;-
printf("\n, = %d",punctuation[0]);
printf("\n. = %d",punctuation[1]);
printf("\n; = %d",punctuation[2]);
printf("\n- = %d",punctuation[3]);
}
/*replace word*/
char *replace(char *st, char *orig, char *repl)
{
static char buffer[2000];
char *ch;
if (!(ch = strstr(st, orig)))
return st;
strncpy(buffer, st, ch-st);
buffer[ch-st] = 0;
sprintf(buffer+(ch-st), "%s%s", repl, ch+strlen(orig));
return buffer;
}
This is the output I'm getting: http://pastebin.com/8qSPQkiM

I'd start by changing this:
entry->word = malloc(sizeof(char)*(strlen(word)+1));
entry->word = word;
to this:
entry->word = malloc(sizeof(char)*(strlen(word)+1));
strcpy(entry->word, word);
As it is now, you're leaking memory and storing a stack-var pointer, which eventually is invalid. You're code has other problems, but this is likely the most immediate problem you're facing.
Next, in insert(), you're incorrectly resetting counters on intermediate nodes. This:
//if it is lower, place on the left
if(result<0)
{
insert(&(*dictionary)->left, entry);
(*dictionary)->count=1; // <=== SHOULD NOT BE HERE
}
//if it is higher, place on the right
if(result>0)
{
insert(&(*dictionary)->right, entry);
(*dictionary)->count=1; // <=== SHOULD NOT BE HERE
}
//if it is equal, don't create a new entry but update the count
if(result == 0)
{
(*dictionary)->count++;
}
should be this:
if(result<0)
{
insert(&(*dictionary)->left, entry);
}
else if (result>0)
{
insert(&(*dictionary)->right, entry);
}
else
{ // found it. update the count
(*dictionary)->count++;
}

Related

Cannot implement Linked List in the given code

I want to insert the data in ascending order based on the partNumber.
When the function is called in main, then the node is successfully added at the first position. But on calling the function second time, there is some problem in insertion and I am unable to figure it out. When I enter the values(in second call), I get the error
Process exited after 8.277 seconds with return value 3221225477
typedef struct part {
int partNumber;
char partName[200];
int partQuantity;
struct part *nextPart;
} Part;
Part *inventory = NULL;
void insertPart();
int
main(int argc, char *argv[])
{
insertPart();
insertPart();
insertPart();
insertPart();
return 0;
}
void
insertPart()
{
Part *tempPart,
*traversePart,
*swapPart;
int counter = 0;
traversePart = inventory;
tempPart = (Part *) malloc(sizeof(Part *));
printf("Enter the Part Number\n");
scanf("%d", &(tempPart->partNumber));
getchar();
printf("Enter the Part Name\n");
fgets(tempPart->partName, 200, stdin);
printf("Enter the Part Quantity\n");
scanf("%d", &(tempPart->partQuantity));
getchar();
if (inventory == NULL) {
inventory = tempPart;
printf("Part added at the first position.\n");
}
else {
while (traversePart->nextPart->partNumber < tempPart->partNumber) {
counter++;
traversePart = traversePart->nextPart;
if (traversePart->nextPart == NULL) {
break;
}
}
if (counter == 0) {
swapPart = inventory;
inventory = tempPart;
tempPart->nextPart = swapPart;
}
else if (traversePart->nextPart == NULL) {
traversePart->nextPart = tempPart;
}
else {
swapPart = traversePart->nextPart;
traversePart->nextPart = tempPart;
tempPart->nextPart = swapPart;
}
}
printf("Element added at position : %d", counter);
}
The problem is traversePart->nextPart->partNumber traversePart->nextPart is not referring to anything or it is not holding any of the address. When you insert first value if condition is true
if (inventory == NULL) {
inventory = tempPart;
printf("Part added at the first position.\n");
}
inventory now holding the address of tempPart but while assigning values of tempPart you never assign an address to its nextvalue and it's not there because you only inserted the first value. For the second position
else{
while(traversePart->nextPart!=NULL)
{
traversePart=traversePart->nextPart;
}
if(traversePart->partNumber < tempPart->partNumber){
//here you can verify conditions
traversePart->nextPart = tempPart
}
}
You're intermixing fgets and scanf [and getchar]. Better to use just fgets and then apply strtol for numbers [or sscanf].
You're linked list code is a bit convoluted. It can be simplified.
Here's the refactored code. I've pulled some helper functions that I had lying around to do the prompting.
And, I added list printing.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
typedef struct part {
int partNumber;
char partName[200];
int partQuantity;
struct part *nextPart;
} Part;
Part *inventory = NULL;
void insertPart();
int getstr(char *buf,int buflen,const char *prompt);
long getnum_strtol(const char *prompt);
int
main(int argc, char **argv)
{
insertPart();
insertPart();
insertPart();
insertPart();
for (Part *cur = inventory; cur != NULL; cur = cur->nextPart)
printf("partNumber=%d partQuantity=%d partName='%s'\n",
cur->partNumber,cur->partQuantity,cur->partName);
return 0;
}
void
insertPart(void)
{
Part *tempPart;
Part *cur;
Part *prev = NULL;
int counter = 0;
#if 0
tempPart = (Part *) malloc(sizeof(Part *));
#else
tempPart = malloc(sizeof(*tempPart));
#endif
tempPart->partNumber = getnum_strtol("Enter the Part Number");
getstr(tempPart->partName,sizeof(tempPart->partName),"Enter the Part Name");
tempPart->partQuantity = getnum_strtol("Enter the Part Quantity");
tempPart->nextPart = NULL;
// find the tail/end of the list
for (cur = inventory; cur != NULL; cur = cur->nextPart) {
++counter;
// insert in sorted part order
if (cur->partNumber > tempPart->partNumber)
break;
prev = cur;
}
do {
tempPart->nextPart = cur;
// insert in the middle or end of list
if (prev != NULL) {
prev->nextPart = tempPart;
break;
}
// insert in new list or before first element of existing list
tempPart->nextPart = inventory;
inventory = tempPart;
} while (0);
printf("\nElement added at position : %d\n", counter);
}
// getstr -- get a string with prompt
// RETURNS: length or (<0 -> error)
int
getstr(char *buf,int buflen,const char *prompt)
{
char *cp;
int ret = 0;
// NOTE: usage of the error codes in errno.h is arbitrary
while (ret <= 0) {
// ensure buffer has enough space
if (buflen < 2) {
ret = -ENOMEM;
break;
}
// output prompt
if (prompt != NULL) {
printf("%s: ",prompt);
fflush(stdout);
}
// get a line
cp = fgets(buf,buflen,stdin);
// EOF
if (cp == NULL) {
ret = -ENODATA;
break;
}
// get buffer length
ret = strlen(buf);
// empty string
if (ret <= 0)
continue;
// point to last char
cp = &buf[ret - 1];
// ensure we got a newline -- if not, fgets had to chop the line (i.e.)
// the line is too long to fit in the buffer
if (*cp != '\n') {
ret = -ENOSPC;
break;
}
// strip the newline -- we are done
*cp = 0;
--ret;
}
return ret;
}
// getnum_strtol -- get number using strtol
long
getnum_strtol(const char *prompt)
{
int len;
int readflg = 1;
char *cp;
char buf[100];
long num = 0;
while (readflg) {
len = getstr(buf,sizeof(buf),prompt);
if (len < 0)
exit(1);
num = strtol(buf,&cp,10);
// ensure we got a least one digit
if (cp <= buf)
continue;
switch (*cp) {
case ' ':
case '\t':
case 0:
readflg = 0;
break;
default:
printf("getnum_strtol: not a valid number -- buffer '%s', invalid '%s'\n",
buf,cp);
break;
}
}
return num;
}
Here's the input file I used to test:
37
Hex Bolt
12
28
Machine Screw
6
23
Brad Nail
1000
27
Lock Nut
300
Here's the program output:
Enter the Part Number: Enter the Part Name: Enter the Part Quantity:
Element added at position : 0
Enter the Part Number: Enter the Part Name: Enter the Part Quantity:
Element added at position : 1
Enter the Part Number: Enter the Part Name: Enter the Part Quantity:
Element added at position : 1
Enter the Part Number: Enter the Part Name: Enter the Part Quantity:
Element added at position : 2
partNumber=23 partQuantity=1000 partName='Brad Nail'
partNumber=27 partQuantity=300 partName='Lock Nut'
partNumber=28 partQuantity=6 partName='Machine Screw'
partNumber=37 partQuantity=12 partName='Hex Bolt'

Searching, inserting, outputing from binary search tree.

How can i do above things in one single programme with multiple duplicates keys and csv. type input data file?
EDIT: assumed KEY "," DATA "\n" (no whitespace, for brevity) and appended a short sketch at the end such that the OP has a start.
I cannot repair it because I don't know the input but I can point out some of the errors you made. See comments in code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXNAMELENTH 64
#define MAXDATALENTH 1465
typedef struct node {
char name[MAXNAMELENTH];
char data[MAXDATALENTH];
struct node *left;
struct node *right;
} node;
node *root;
node *search(node ** tree, char *key, int count, FILE * fp_out);
node *insertion(node * r, char *key, char *value);
void deltree(node * tree);
char *strtok_r(char *str, const char *delim, char **nextp);
int main(int argc, char *argv[])
{
node *root;
node *tmp;
FILE *fp;
FILE *outputfile;
FILE *keyfile;
FILE *fp_key;
int i;
int counter = 0;
int bufsize = MAXDATALENTH + MAXNAMELENTH;
int keyfilelen = MAXNAMELENTH;
char *keyread;
char *buffer, *saveptr;
char *line = NULL;
char *keyin = NULL;
char *valuein = NULL;
char inputkey[MAXNAMELENTH];
char *target_key = NULL;
char *keyline = NULL;
root = NULL;
/* Inserting nodes into tree */
buffer = (char *) malloc(bufsize * sizeof(char));
if (buffer == NULL) {
// use the proper macros, please
exit(EXIT_FAILURE);
}
// Check outputs! Always check outputs!
// I just assume that it has not been done here for simplicity
// and fear of posting-size limits (limit is 30k IIRC, that's a lot of code)
fp = fopen(argv[1], "r");
outputfile = fopen("outputfile.txt", "a");
// this is an infinite loop. How do you break out?
while (1) {
// fgets() returns NULL in case of EOF and error
// (yes, both, so check, e.g.: with ferror())
fgets(line, bufsize, fp);
buffer = line;
// I'm not fully sure, but I think you wanted a "=" instead of a ","
// Please switch on warnings for your compiler
keyin, strtok_r(buffer, ",", &saveptr);
// NUL delimited? Really? Are you sure strtok_r() can handle it?
valuein = strtok_r(NULL, "\0", &saveptr);
// insertion() returns a node
insertion(root, keyin, valuein);
}
/* Search node into tree */
// Wrong type: keyfile is a filepointer (FILE *), not a string (char *)
if (scanf("%s", keyfile) != EOF) {
// hu? Please switch on warnings for your compiler
keyfile = fopen(argv[4], "r");
// where's keyfile? Gone?
while ((fgets(keyline, keyfilelen, fp_key))) {
keyread = strtok_r(keyline, "\n", &saveptr);
search((&root), keyread, counter, outputfile);
}
// Please use braces, even here, thank you
if (keyline) {
free(keyline);
}
fclose(keyfile);
}
// This reads keys given as arguments at programstart,
// that's not what the teacher wanted
for (i = 3; argv[i] != NULL; i++) {
target_key = argv[i];
search((&root), target_key, counter, outputfile);
}
// This program does not read from stdin.
// Hint: stdin is also nothing more (although slightly less) than a file
// but it is already open and you don't close it
// (but it may not exist at all, so check first)
/* Deleting all nodes of tree */
deltree(root);
fclose(fp);
fclose(outputfile);
return 0;
}
node *insertion(node * r, char *key, char *value)
{
if (r == NULL) // BST is not created created
{
r = (node *) malloc(sizeof(node)); // create a new node
// insert data to new node
strcpy(r->name, key);
strcpy(r->data, value);
// make left and right childs empty
r->left = NULL;
r->right = NULL;
}
// if the data is less than node value then we must put this in left sub-tree
else if (strcmp(key, r->name) < 0) {
r->left = insertion(r->left, key, value);
}
// else this will be in the right subtree
else if (strcmp(key, r->name) > 0) {
r->right = insertion(r->right, key, value);
} else {
if (strcmp(value, r->data) > 0) {
r->left = insertion(r->left, key, value);
} else if (strcmp(value, r->data) < 0) {
r->right = insertion(r->right, key, value);
}
// what if both, name and data are equal?
}
// if you don't want to do anything with the returned
// node than don't return it. Just an int indicating error
// or something in the line
return r;
}
void deltree(node * tree)
{
if (tree) {
deltree(tree->left);
deltree(tree->right);
free(tree);
}
}
// searching does not compare the same way as inserting
node *search(node ** tree, char *key, int count, FILE * fp_out)
{
if (!(*tree)) {
return NULL;
}
if (strcmp(key, (*tree)->name) < 0) {
search(&((*tree)->left), key, count, fp_out);
count++;
} else if (strcmp(key, (*tree)->name) > 0) {
search(&((*tree)->right), key, count, fp_out);
count++;
} else if (strcmp(key, (*tree)->name) == 0) {
// won't print anything, because you return before the printing
return *tree;
fprintf(fp_out, "%s --> %s\n", key, (*tree)->data);
} else {
fprintf(fp_out, "%s --> NOTFOUND", key);
}
printf("%s --> %s\n", key, count);
// nothing to return here?
}
// Thank you for the proper reference!
// Seems to grow less and less usual these times.
/*
* public domain strtok_r() by Charlie Gordon
*
* from comp.lang.c 9/14/2007
*
* http://groups.google.com/group/comp.lang.c/msg/2ab1ecbb86646684
*
* (Declaration that it's public domain):
* http://groups.google.com/group/comp.lang.c/msg/7c7b39328fefab9c
*/
char *strtok_r(char *str, const char *delim, char **nextp)
{
char *ret;
if (str == NULL) {
str = *nextp;
}
str += strspn(str, delim);
if (*str == '\0') {
return NULL;
}
ret = str;
str += strcspn(str, delim);
if (*str) {
*str++ = '\0';
}
*nextp = str;
return ret;
}
EDIT: the sketch. Please be aware that I didn't do all of your homework, I left a bit "for the student", as the saying goes.
/*
*
* - Construct a binary search tree to store the information contained in the file specified
* in the command line argument. Each record should be stored in a separate Node.
*
* - Search the binary search tree for records, based on their keys. The keys are read in
* from stdin, i.e. from the screen.
*
* For testing, it is often convenient to create a file of keys to be searched, one per line, and redirect the input from this file.
*
* Use the UNIX operator < for redirecting input from a file.
*
* - Examples of use:
* ./yelp1 datafile outputfile then type in keys;
* or
* ./yelp1 datafile outputfile < keyfile
*
* - Your program will look up each key and output the information (the data found) to the output
* file specified by the second command line parameter.
*
* If the key is not found in the tree,you must output the word NOTFOUND.
*
* The number of key comparisons performed during both successful and unsuccessful lookups
* have to be written to stdout.
*
* - Remember that the entries in the file do not necessarily have unique keys.
* Your search must locate all keys matching the search key, and output all the data found.
*/
/*
* Tested with datafile produced by the following shellcode
*
* # KEY "," DATA
* counter=1;
* while [ $counter -le 100 ];
* do counter=$((counter + 1));
* echo $(pwgen 10 1)","$(pwgen 10 1) >> testtree.db
* done;
*
* # ten random KEY
* shuf -n10 testtree.db | sed -e 's/,[a-z]\+//g' > testtree10keys
*
* # ten random DATA
* shuf -n10 testtree.db | sed -e 's/[a-z]\+,//g' > testtree10datas
*
* Please insert doublettes manually
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// data produced has only 10 characters each (plus NUL)
#define MAXNAMELENTH 11
#define MAXDATALENTH 11
typedef struct node {
char name[MAXNAMELENTH];
char data[MAXDATALENTH];
struct node *left;
struct node *right;
} node;
node *root;
node *search(node ** tree, char *key, int *count, FILE * fp_out);
int insertion(node ** r, char *key, char *value);
void deltree(node * tree);
//char *strtok_r(char *str, const char *delim, char **nextp);
void bt_print(node * leaf);
int main(int argc, char *argv[])
{
node *root;
// node *tmp;
FILE *fp;
FILE *outputfile;
// FILE *keyfile;
// FILE *fp_key;
// int i;
int counter = 0;
int bufsize = MAXDATALENTH + MAXNAMELENTH + 2;
// int keyfilelen = MAXNAMELENTH;
// char *keyread;
char *buffer;
// char *saveptr;
// char *line = NULL;
char *keyin = NULL;
char *valuein = NULL;
// char inputkey[MAXNAMELENTH];
// char *target_key = NULL;
// char *keyline = NULL;
char *e;
int res;
char *comma;
root = NULL;
// must have at least one argument
if (argc < 2) {
fprintf(stderr, "Usage: %s datafile [< key-list]\n", argv[0]);
exit(EXIT_FAILURE);
}
/* Inserting nodes into tree */
buffer = malloc(bufsize);
if (buffer == NULL) {
fprintf(stderr, "Malloc failed\n");
exit(EXIT_FAILURE);
}
fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "Opening \"%s\" for reading failed\n", argv[1]);
exit(EXIT_FAILURE);
}
outputfile = fopen("outputfile.txt", "a");
if (fp == NULL) {
fprintf(stderr, "Opening \"outputfile.txt\" for appending failed\n");
exit(EXIT_FAILURE);
}
while ((e = fgets(buffer, bufsize, fp))) {
if (e == NULL) {
if (ferror(fp)) {
// TODO: check errno for the exact kind of error
fprintf(stderr, "An eror occured during reading \"%s\"\n", argv[1]);
exit(EXIT_FAILURE);
} else if (feof(fp)) {
break;
}
}
// assuming strict KEY","DATA"\n" and without whitespace
// otherwise you need to check every step here!
// valuein points to the comma before DATA
valuein = strchr(buffer, ',');
// valuein points to DATA
valuein++;
// DATA is 10 characters large
valuein[10] = '\0';
// comma points to the comma before DATA
comma = strchr(buffer, ',');
// make *comma NUL
*comma = '\0';
keyin = buffer;
// ignoring return for now
//printf("%s,%s\n",keyin, valuein);
(void) insertion(&root, keyin, valuein);
}
bt_print(root);
// search-keys come from either stdin or get typed in.
// things typed in are also stdin
while ((res = scanf("%10s", buffer)) == 1) {
//printf("%s\n",buffer);
search(&root, buffer, &counter, outputfile);
printf("Misses for KEY \"%s\" = %d\n", buffer, counter);
counter = 0;
}
/* Deleting all nodes of tree */
deltree(root);
fclose(fp);
fclose(outputfile);
return 0;
}
void bt_print(node * leaf)
{
if (leaf) {
printf("%s,%s\n", leaf->name, leaf->data);
bt_print(leaf->left);
bt_print(leaf->right);
}
}
int insertion(node ** r, char *key, char *value)
{
if (*r == NULL) {
*r = malloc(sizeof(node));
if (r == NULL) {
return 0;
}
strcpy((*r)->name, key);
strcpy((*r)->data, value);
(*r)->left = NULL;
(*r)->right = NULL;
}
// Checks for returns omitted for clarity
else if (strcmp(key, (*r)->name) < 0) {
insertion(&(*r)->left, key, value);
}
// else this will be in the right subtree
else if (strcmp(key, (*r)->name) > 0) {
insertion(&(*r)->right, key, value);
} else {
if (strcmp(value, (*r)->data) > 0) {
insertion(&(*r)->left, key, value);
} else if (strcmp(value, (*r)->data) < 0) {
insertion(&(*r)->right, key, value);
}
// what if both, name *and* data are equal?
return 0;
}
return 1;
}
void deltree(node * tree)
{
if (tree) {
deltree(tree->left);
deltree(tree->right);
free(tree);
}
}
// search still cannot find multiple occurences of KEY
// Can you repair it?
node *search(node ** tree, char *key, int *count, FILE * fp_out)
{
node *tmp;
if (!(*tree)) {
printf("%s --> NOTFOUND\n", key);
fprintf(fp_out, "%s --> NOTFOUND", key);
return NULL;
} else {
if (strcmp(key, (*tree)->name) < 0) {
tmp = search(&((*tree)->left), key, count, fp_out);
(*count)++;
return tmp;
} else if (strcmp(key, (*tree)->name) > 0) {
tmp = search(&((*tree)->right), key, count, fp_out);
(*count)++;
return tmp;
} else {
// HINT 1: check (*tree)->left->name and (*tree)->right->name
// (recursively) and print it as long as key==name
// HINT 2: you don't need recursion, you can do it in a loop
printf("FOUND KEY %s --> DATA %s\n", key, (*tree)->data);
fprintf(fp_out, "%s --> %s\n", key, (*tree)->data);
return *tree;
}
}
}

How would I read a text file in C?

I have file.txt with
123456 2,00 beer
234567 2,50 milk
345678 3,30 ice cream
I want to put this info in my dynamic two-dimensional array:
char **dataBase;
dataBase = (char**)malloc(NUM_OF_PROD * sizeof(char*));
for(i = 0; i < NUM_OF_PROD; i++){
dataBase[i] = (char*)malloc(MAX_BUFFER* sizeof(char));
}
But I don't know how. We have here 3 lines. If it was a C++, I would use getline() but in this situation I can't find a solution.
I usually use the fgets() function to a file on a line-per-line basis (provided it is a text file).
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#define LINELEN 200
#define NAMELEN 40
struct PRICELIST
{
char item[NAMELEN];
float price;
unsigned int order_no;
struct PRICELIST *next;
struct PRICELIST *prev;
};
void list_print_node (struct PRICELIST *node)
{
printf ("%d %4.2f %s\n", node->order_no, node->price, node->item);
}
void list_print (struct PRICELIST *head)
{
printf ("Order # Price Item\n");
printf ("------------------------------\n");
while (head)
{
list_print_node (head);
head = head->next;
}
}
void list_delete (struct PRICELIST *head)
{
if (head)
{
/* recursive call */
list_delete (head->next);
free (head);
}
}
struct PRICELIST *list_read (char *filename)
{
FILE *file;
char line[LINELEN];
struct PRICELIST *pricelist, *node, *prev;
char *p;
size_t len;
file = fopen (filename, "r");
if (file == NULL)
{
perror (filename);
return NULL;
}
pricelist = NULL;
prev = NULL;
while (1)
{
if (fgets (line, sizeof(line), file) == NULL)
break;
/* eat the newline at the end of the buffer, be CR/CRLF agnostic .. */
len = strlen (line) - 1;
if (line[len] == '\r' || line[len] == '\n')
{
line[len] = '\0';
len --;
}
if (line[len] == '\r' || line[len] == '\n')
line[len] = '\0';
/* allocate a new node in the list */
node = malloc (sizeof (struct PRICELIST));
if (node)
{
/* now use sscanf() for getting single elements */
sscanf (line, "%d %f", &node->order_no, &node->price);
/* since the item name might contain spaces this is not so easy .. */
p = line;
while (isspace(*p)) p++;
while (isdigit(*p)) p++;
while (isspace(*p)) p++;
while (isdigit(*p)) p++;
while (ispunct(*p)) p++;
while (isdigit(*p)) p++;
while (isspace(*p)) p++;
strncpy (node->item, p, sizeof(node->item));
node->next = NULL;
/* if this is the first node of the list assign the head to it */
if (pricelist == NULL)
pricelist = node;
/* append the new node to the end of the linked list */
if (prev)
prev->next = node;
node->prev = prev;
/* save it for the next entry */
prev = node;
}
}
/* we are done with the file, close it */
fclose (file);
return pricelist;
}
/* let's test it */
int main (int argc, char *argv[])
{
struct PRICELIST *pricelist;
if (argc < 2)
{
printf ("Usage: %s filename\n", argv[0]);
return 0;
}
pricelist = list_read (argv[1]);
if (pricelist)
{
/* print the list */
printf ("This is the price list (filename '%s'):\n\n", argv[1]);
list_print (pricelist);
/* delete the list */
list_delete (pricelist);
}
return 0;
}
In the comments you mentioned you were only concerned about actually reading a file.
Here's how you'd go about reading a file (currently untested, binary mode):
#include <stdio.h>
int main()
{
FILE *file = fopen("path/to/your/file/yourfile.txt", "rb");
if(!file) return 1; //something went wrong!
long size = fseek(file, 0, SEEK_END);
char *buf = malloc(size);
fread(&buf, size, 1, file); //read all contents, once
fclose(file);
free(buf); //because this is just an example
return 0;
}
For more info on reading a file, just do a quick google search and you'll find almost everything you're looking for.
You can implement your own version of getline using fgetc and realloc.
#include <stdio.h>
#include <stdlib.h>
char *getline(FILE *file)
{
size_t size = 16; // Size of memory allocated for line
size_t len = 0; // Characters read
char *line = malloc(size);
// Return NULL if memory allocation fails
if (line == NULL)
return NULL;
for(;;) {
int c;
switch (c = fgetc(file)) {
// If End Of File is met, return the line up until this point
// if anything has been read
case EOF:
if (len == 0) {
free(line);
return NULL;
}
else {
line[len+1] = '\0';
return line;
}
case '\n':
line[len+1] = '\0'; // NUL terminate the string
return line;
default:
line[len++] = c;
}
// If the string plus NUL terminator is longer than size
// double the size of line
if (len + 1 >= size) {
size *= 2;
line = realloc(line, size);
// Return NULL if memory allocation fails
if (line == NULL)
return NULL;
}
}
}
There are also many free/open source implementations of the same function that can be found online. For instance this GPL 2 one. If you are on a POSIX system (e.g. OS X or Linux) there is already a version of getline found in stdio.h.

strcmp() string from stdin and string from file [duplicate]

This question already has an answer here:
strcmp doesnt give 0, but the two args are the same
(1 answer)
Closed 8 years ago.
I have a problem with comparing string from file.
I want to create a list of words from a file which is a dictionary. I don't know why strcmp() return only -1 or 1 even when I use a word from my file. On output I have for example: 1somethingsomething instead of 0somethingsomething
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct words
{
char *word;
struct words *next;
};
void pushBack(struct words **headPointer, char string[])
{
struct words *pointer;
pointer = *headPointer;
if (*headPointer == NULL)
{
*headPointer = (struct words*)malloc(sizeof(struct words));
(*headPointer)->next = NULL;
(*headPointer)->word = (char*)malloc(sizeof(char)*(strlen(string)+1));
strcpy((*headPointer)->word, string);
}
else
{
while (pointer->next != NULL)
{
pointer = pointer->next;
}
pointer->next = (struct words*)malloc(sizeof(struct words));
pointer = pointer->next;
pointer->next = NULL;
pointer->word = (char*)malloc(sizeof(char)*(strlen(string)+1));
strcpy(pointer->word, string);
}
}
void createList(struct words **headPointer)
{
FILE *fp;
char string[80];
if ((fp = fopen("polski.txt", "rw")) == NULL)
{
printf ("Nie mogê otworzyæ pliku test.txt do zapisu!\n");
exit(-1);
}
else
{
while(fgets(string, 80, fp) != NULL)
{
pushBack(headPointer, string);
}
}
}
int seek(struct words *head, struct words **wordBeforePointer, struct words **wordAfterPointer)
{
char string[80];
printf("Type a word to seek:\n");
scanf("%s", string);
*wordBeforePointer = NULL;
*wordAfterPointer = NULL;
if (head != NULL)
{
if (strcmp(head->word, string) == 0)
{
return 1;
}
while(head->next != NULL)
{
head = head->next;
printf("%s", string);
printf("%s", head->word);
printf("%d", strcmp(head->word, string));
if (strcmp(head->word, string) == 0)
{
return 1;
}
}
}
return 0;
}
int main()
{
struct words *head, *wordBefore, *wordAfter;
head = NULL;
wordBefore = NULL;
wordAfter = NULL;
createList(&head);
printf("%d", seek(head, &wordBefore, &wordAfter));
return 0;
}
The fgets call does not actually remove the trailing newline, so people using this method frequently find that strcmp does not work simply because:
"thisword\n" != "thisword"
If you want to strip it manually, you can use something like:
while (fgets (inputLine, 80, filePtr) != NULL) {
// Get size of input line.
size_t strSize = strlen (inputLine);
// If there's a newline at the end, remove it.
if ((strSize > 0) && (inputLine[strSize-1] == '\n'))
inputLine[strSize-1] = '\0';
// Do whatever you need to "non-newline" line.
doSomethingWith (inputLine);
}

My Linked List does not print out from the beginning

The problem I am having with my program is that I create a linked list, but when I go to print out my linked list it just prints out the last person in the list. It is supposed to print out everyone in the list. This is the only problem that I have in my program and if someone could point me in the right direction it would be greatly appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define MAX_PLAYERS 9
#define MAX_STR_LEN 25
typedef struct Player_ {
char name[MAX_STR_LEN];
int number_of_hits;
int number_of_strikes;
struct Player_ *next;
} Player;
int pitch_result(void);
Player *add_player(Player *first_player, const char *name);
void destroy_team(Player *first_player);
void display_line_up(Player* first);
void readFile(Player **lineup, const char* file);
int main(int argc, char *argv[]) {
if(argc < 2){
exit(1);
}
srand(time(NULL));
int strikes = 0;
int hits = 0;
Player *lineup = NULL;
readFile(&lineup, argv[1]);
printf("\nLine up for this team is: ");
display_line_up(lineup);
printf("\n\n%s is batting \n", lineup->name);
for ( ; strikes < 3; ) {
int result = pitch_result();
if (result) {
++hits;
}
else
++strikes;
}
printf("\nScore of game was: %d\n", hits/4);
return 0;
}
void readFile(Player **lineup, const char* file){
FILE *input;
input = fopen(file, "r");
if(input == NULL){
printf("Failed to open");
}
char player[MAX_STR_LEN];
while(fscanf(input, "%s", &player) != EOF){
// printf("\n%s", player);
*lineup = add_player(*lineup, player);
}
}
int pitch_result(void) {
int result;
result = rand() % 2;
return result;
}
void destroy_team(Player *first_player){
Player *temp = first_player->next;
Player *free_player;
while (temp) {
free_player = temp;
temp = temp->next;
free(free_player->name);
free(free_player);
}
}
Player *add_player(Player *first_player, const char *name) {
Player *new_player = (Player*)malloc(sizeof(Player));
int nameLength;
nameLength = strlen(name);
// new_player->name = malloc(nameLength * sizeof(char));
new_player->next = NULL;
strncpy(new_player->name, name, nameLength);
new_player->number_of_hits = 0;
new_player->number_of_strikes = 0;
return new_player;
}
void display_line_up(Player *first){
if(first == NULL)
printf("\nThe list is empty");
else {
while(first != NULL){
printf("\n%s", first->name);
first = first->next;
}
}
}
Here is the file that I read into the program when I run it (stats.txt)
Yadier_Molina
Allen_Craig
Daniel_Descalso
Rafael_Furcal
David_Freese
Matt_Holiday
Jon_Jay
Carlos_Beltran
Matt_Adams
This is the sample output that I was given for my program
Line up for this team is:
Yadier_Molina
Allen_Craig
Daniel_Descalso
Rafael_Furcal
David_Freese
Matt_Holliday
Jon_Jay
Carlos_Beltran
Matt_Adams
Yadier_Molina is batting
Strike!
hit!
Strike!
Strike!
Score of game was: 0
Your add_player function returns a pointer to a node that contains the new player-- and has no connection to the preexisting list (the variable first_player is never even used).
Your readFile function uses this, discarding the old list each time it reads the name of a new player:
*lineup = add_player(*lineup, player);
so the resultant list contains only the last player.
As Beta said, you are not concatenating the players to the list. You can keep a pointer to the last element, which you modify from readFile:
void readFile(Player **lineup, const char* file){
Player* last = NULL;
FILE *input;
input = fopen(file, "r");
if(input == NULL){
printf("Failed to open");
}
char player[MAX_STR_LEN];
while(fscanf(input, "%s", &player) != EOF){
// printf("\n%s", player);
Player* p = add_player(player);
if (!last)
*lineup = p;
else
last->next = p;
last = p;
}
}
And the function add_player just creates a new player so, I would rename it to: create_player(const char* name)

Resources