C: Linked list word frequency - Standard input - c

Edit: Fixed duplication error in code.
I attempted to create a word frequency analysis program that reads in from standard input.
I have two questions.
Currently I am using '\n' to indicate when my program should stop reading in input, I need it to read until the user is done typing. Would it be better to use EOF or the null terminator '\0'
This may be a dumb question but I cannot figure out what is wrong with my output it doubles the letters up every time.
Example input: "This is a test test of the program for frequency is a this for for"
Output:
thhiiss 1
iiss 2
aa 2
tteesstt 2
ooff 1
tthhee 1
pprrooggrraamm 1
ffoorr 3
ffrreeqquueennccyy 1
tthhiiss 1
As you can see the count close to correct for each word, but cannot figure out why the letters are duplicating.
Here is the code I have used:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "list.h"
#define MAXWORD 100
//===========================================================================
struct lnode {
struct lnode *next;
struct lnode *counter;
struct lnode *pLast;
struct lnode *prev;
struct lnode *head;
char *word;
int line;
int count;
int freq;
};
struct lnode *start = NULL;
//===========================================================================
struct lnode *createWordCounter(char *str)
{
struct lnode *pCounter = NULL;
pCounter = (struct lnode*)malloc(sizeof(struct lnode));
pCounter->word = (char*)malloc(strlen(str)+1);
strcpy(pCounter->word, str);
pCounter->freq = 1;
pCounter->next = NULL;
return pCounter;
}
//===========================================================================
void addWord(char *str)
{
struct lnode *pCounter = NULL;
struct lnode *pLast = NULL;
if(start == NULL)
{
start = createWordCounter(str);
return;
}
// If the word is in the list, increment its count
pCounter = start;
int temp = pCounter->freq;
while(pCounter != NULL)
{
if(strcmp(str, pCounter->word) == 0)
{
pCounter->freq++;
return;
}
pLast = pCounter;
pCounter = pCounter->next;
}
// Word is not in the list, add it
pLast->next = createWordCounter(str);
}
//===========================================================================
int getNextWord(char *buf, int bufsize) {
char *p = buf;
char ch;
do {
ch = getchar();
if (ch == '\n')
return 0;
} while (!((ch >= 'A' && ch <= 'Z')||( ch >= 'a' && ch <= 'z')));
do {
if (p - buf < bufsize - 1){
if( ch >= 97 && ch <= 122)//making the ch lowercase if needed
*p++ = ch;
else{ch += 32;
*p++ = ch;}
}//End of if
ch = getchar();
} while (((ch >= 'A' && ch <= 'Z')||( ch >= 'a' && ch <= 'z')));
*p = '\0';
return 1;
}
//===========================================================================
void show(struct lnode *pWord)
{
printf("%s %i\n", pWord->word, pWord->freq);
}
//===========================================================================
int main(){
struct lnode *counter = NULL;
int size = 1000;
char buf[MAXWORD];
while(getNextWord(buf, size) != 0 ){
addWord(buf);
}
counter = start;
while(counter != NULL)
{
show(counter);
counter = counter->next;
}
counter = start;
while(counter != NULL)
{
free(counter->word);
start = counter;
counter = counter->next;
free(start);
}
return 0;
}
This is my first time posting so please let me know if I did anything wrong. Any help is appreciated.
Thanks.

Look at this carefully, it is assigning ch twice to *p
if( ch >= 97 && ch <= 122)//making the ch lowercase if needed
*p++ = ch;
else{ch += 32;}
*p++ = ch;
I think the trailing "}" on the else statement is misplaced.
if( ch >= 97 && ch <= 122) { //making the ch lowercase if needed
*p++ = ch;
} else {
ch += 32;
*p++ = ch;
}
Also, your code will be greatly more readable if you learn about the functions
isalpha, islower, isupper, tolower, toupper. man ctype for info.

Would it be better to use EOF or the null terminator '\0' ?
Use EOF because if you press ctl+D getchar() consider input as EOF . You can also use \n or Use an OR logic with \n,EOF
what is wrong with my output it doubles the letters up every time ?
See this below code in function getNextWord()
if( ch >= 97 && ch <= 122)//making the ch lowercase if needed
*p++ = ch;
//this checks if input character is lowercase character, then store it into buffer
else{ch += 32;} // if input character is lowercase character, won't execute else part
*p++ = ch;
// now again you are copying same input character into buffer next location
Modify above part.

Related

Struct array in c gives the same value to all values. [C]

I read words from the file. When I throw them into the structure, it writes the same values.
What is Problem and How can I fix
Ide: VsCode
Compiler: mingw64-gcc-g++
File Content;
{Sam}
{Patrick}
{Philips}
My Code;
struct Sentence
{
char *word;
};
struct Sentence *words[20];
void readFile(const char *path, char *fileName)
{
int wordpointer = 0;
int len = strlen(fileName);
FILE *fp;
if ((fp = fopen((path), "r")) != NULL)
{
char ch = fgetc(fp);
while (ch != EOF)
{
if (ch == '{')
{
int counter = 0;
while (ch != EOF)
{
char word[20];
ch = fgetc(fp);
if (ch == '}')
{
//printf("%s\n",word);
struct Sentence *st = malloc(sizeof(struct Sentence));
st->word = word;
words[wordpointer] = st;
wordpointer++;
break;
}
word[counter++] = ch;
}
}
ch = fgetc(fp);
}
fclose(fp);
}
for (int i = 0; i < wordpointer; i++)
printf("%s\n", words[i]->word);
}
I can get proper output in the printf function in the comment line, but when I print the Struct, all the values ​​as below are the last word in the file.
Output;
Philips
Philips
Philips
In this while loop
while (ch != EOF)
{
char word[20];
//...
all pointers st->word = word; points to the same local variable word
if (ch == '}')
{
//printf("%s\n",word);
struct Sentence *st = malloc(sizeof(struct Sentence));
st->word = word;
words[wordpointer] = st;
wordpointer++;
break;
}
declared like
st->word = word;
So after exiting the while loop the pointers will be invalid.
You need to allocate memory for each string and copy there entered strings. Moreover you need to append them with the terminating zero character '\0'.

malloc() in C returns populated memory

char *string = (char *) malloc(sizeof(char) * sz);
code right before this->void insert_word(word *root, char string1[], int linenumber) { int sz=strlen(string1)<=MAX_WORD_LENGTH?strlen(string1):MAX_WORD_LENGTH; Code block 3 has the entire context
Sometimes malloc() returns a populated memory location while using it.
What bothers me is that this is not random.
(This program consists of taking words from a file and passing them to this function. For THE SAME WORD, the function behaviour(in particular that of malloc()) is different.
For the inputs
string1=0x7fffffffdf10 "lol" root=BST, sz gets a value of 3
The value allocated to string by malloc() is 0x55555555c510 "\340\305UUUU" Why is malloc not pointing to an empty memory location? (This is not random behaviour, it is predictable and repeatable)
Furthermore,this loop runs an infinite amount of time for some reason
while(strcmp(string1,string)!=0)
{
free(string);
string=NULL;
string = (char *) malloc(sizeof(char) * sz);
strncpy(string,string1,sz);
}
MORE RELAVANT CODE
#define MAX_WORD_LENGTH 20
Definition of the structures
typedef struct linkedList
{
int number;
struct linkedList *next;
}list;
typedef struct word_with_count
{
char* string;
list *linenumbers;
struct word_with_count *left;
struct word_with_count *right;
}word;```
[3] ) The function
void insert_word(word *root, char string1[], int linenumber) {
int sz=strlen(string1)<=MAX_WORD_LENGTH?strlen(string1):MAX_WORD_LENGTH;
char *string = (char *) malloc(sizeof(char) * sz);
strncpy(string,string1,sz);
if (root==NULL) {
return;
} else if (strcmp(string, root->string) < 0) {
if (root->left == NULL) {
root->left = createword(string, linenumber);
} else {
insert_word(root->left, string, linenumber);
}
} else if (strcmp(string, root->string) > 0) {
if (root->right == NULL) {
root->right = createword(string, linenumber);
} else {
insert_word(root->right, string, linenumber);
}
} else {
append_list(linenumber, root->linenumbers);
}
free(string);
}
main() which calls this function
int main() {
char path[MAX_PATH_LENGTH];
FILE *fp;
fgets(path, MAX_PATH_LENGTH, stdin);
if (strlen(path) > 0 && path[strlen(path) - 1] == '\n')
path[strlen(path) - 1] = '\0';
fp = fopen(path, "r");
if (fp == NULL) {
printf("File not found\n");
return 0;
}
char ch;
int line_count = 1;
char current_word[MAX_WORD_LENGTH] = "";
word *root = NULL;
while (!feof(fp)) {
ch = fgetc(fp);
//printf("%c", ch);
if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z') {
if (ch >= 'A' && ch <= 'Z')
ch = ch - 'A' + 'a';
strncat(current_word, &ch, 1);
} else if (ch == '-') {
continue;
} else {
if (strlen(current_word) > 2) {
if (root == NULL) {
root = createword(current_word, line_count);
} else {
insert_word(root, current_word, line_count);
}
}
memset(current_word, 0, sizeof(current_word));
if (ch == '\n') {
line_count++;
}
}
}
if (strlen(current_word) > 2) {
if (root == NULL) {
root = createword(current_word, line_count);
} else {
insert_word(root, current_word, line_count);
}
}
fclose(fp);
// print_tree(root);
//printf("\n");
//print_tree(root);
int status=delete_low_ocurrence(root, NULL, 3);
if (status == -1)root = NULL;
print_tree(root);
freetree(root);
return 0;
}
5)Auxilary function used by this function
word* createword(char string[], int linenumber)
{
word *newword = (word*)malloc(sizeof(word));
int sz=strlen(string)<=MAX_WORD_LENGTH?strlen(string):MAX_WORD_LENGTH;
newword->string = (char*)malloc(sizeof(char)*sz);
strncpy(newword->string, string,sz);
newword->linenumbers = (list*)malloc(sizeof(list));
newword->linenumbers->number = linenumber;
newword->linenumbers->next = NULL;
newword->left = NULL;
newword->right = NULL;
return newword;
}
Textfile given as input
much2f
much3f
lol
lol
lol
qwertyuiopasdfghjklzxcvbnmqwertyuiop
qwertyuiopasdfghjklzxcvbnmqwertyuiop
qwertyuiopasdfghjklzxcvbnmqwertyuiop
qwertyuiopasdfghjklzxcvbnmqwertyuiop
Why is malloc not pointing to an empty memory location?
Because it can. The content of the allocated memory via malloc() is not specified.
If code needs zeroed out memory, see calloc().
Bad code
strncpy(string,string1,sz) does not result in string being a string as it may lack null character termination. The following (strcmp(string... is then undefined behavior. Instead, do not use strncpy(), use strcpy() and make certain the prior allocation has enough room for the terminating null character.
strncpy(string,string1,sz);
...
} else if (strcmp(string, root->string) < 0) { // bad
Repaired code
word* createword(const char string[], int linenumber) {
word *newword = calloc(1, sizeof *newword);
size_t length = strlen(string);
if (length > MAX_WORD_LENGTH) {
length = MAX_WORD_LENGTH;
}
char *s = malloc(length + 1); // Include room for the \0
list *linenumbers = calloc(1, sizeof *linenumbers);
// Test allocation success
if (newword == NULL || s == NULL || linenumbers == NULL) {
free(newword);
free(s);
free(linenumbers);
return NULL;
}
memcpy(s, string, length); // Only copy the first 'length' characters.
s[length] = 0;
newword->string = s;
newword->linenumbers = linenumbers;
newword->linenumbers->number = linenumber;
newword->linenumbers->next = NULL;
newword->left = NULL;
newword->right = NULL;
return newword;
}
Why is “while ( !feof (file) )” always wrong?
feof(fp) improperly used here. fgetc() returns 257 different values. Do not use char ch.
//char ch;
//...
//while (!feof(fp)) {
// ch = fgetc(fp);
int ch;
...
while ((ch = fgetc(fp)) != EOF) {;
This is quite normal behaviour. 'malloc' just does the memory allocation, it makes no commitment on what's already in that memory location. What you probably need is 'calloc', which clears the memory and then allocates it to your program.

Remove a word from a sentence (string)

I am in the stage of preparing myself for exams, and the thing that I m least proud of are my skills with strings. What I need to do is remove a word from a sentence, without using <string.h> library at all.
This is what I've got so far. It keeps showing me that certain variables are not declared, such as start and end.
#include <stdio.h>
/* Side function to count the number of letters of the word we wish to remove */
int count(char *s) {
int counter = 0;
while (*s++) {
counter++;
s--;
return counter;
}
/* Function to remove a word from a sentence */
char *remove_word(const char *s1, const char *s2) {
int counter2 = 0;
/* We must remember where the string started */
const char *toReturn = s1;
/* Trigger for removing the word */
int found = 1;
/* First we need to find the word we wish to remove [Don't want to
use string.h library for anything associated with the task */
while (*s1 != '\0') {
const char *p = s1;
const char *q = s2;
if (*p == *q)
const char *start = p;
while (*p++ == *q++) {
counter2++;
if (*q != '\0' && counter2 < count(s2))
found = 0;
else {
const char *end = q;
}
}
/* Rewriting the end of a sentence to the beginning of the found word */
if (found) {
while (*start++ = *end++)
;
}
s1++;
}
return toReturn;
}
void insert(char niz[], int size) {
char character = getchar();
if (character == '\n')
character = getchar();
int i = 0;
while (i < size - 1 && character != '\n') {
array[i] = character;
i++;
character = getchar();
}
array[i] = '\0';
}
int main() {
char stringFirst[100];
char stringSecond[20];
printf("Type your text here: [NOT MORE THAN 100 CHARACTERS]\n");
insert(stringFirst, 100);
printf("\nInsert the word you wish to remove from your text.");
insert(stringSecond, 20);
printf("\nAfter removing the word, the text looks like this now: %s", stringFirst);
return 0;
}
your code is badly formed, i strongly suggest compiling with:
gcc -ansi -Wall -pedantic -Werror -D_DEBUG -g (or similar)
start with declaring your variables at the beginning of the function block, they are known only inside the block they are declared in.
your count function is buggy, missing a closing '}' (it doesn't compile)
should be something like
size_t Strlen(const char *s)
{
size_t size = 0;
for (; *s != '\n'; ++s, ++size)
{}
return size;
}
implementing memmove is much more efficient then copy char by char
I reformatted you code for small indentation problems and indeed indentation problems indicate real issues:
There is a missing } in count. It should read:
/* Side function to count the number of letters of the word we wish to remove */
int count(char *s) {
int counter = 0;
while (*s++) {
counter++;
}
return counter;
}
or better:
/* Side function to count the number of letters of the word we wish to remove */
int count(const char *s) {
const char *s0 = s;
while (*s++) {
continue;
}
return s - s0;
}
This function counts the number of bytes in the string, an almost exact clone of strlen except for the return type int instead of size_t. Note also that you do not actually use nor need this function.
Your function insert does not handle EOF gracefully and refuses an empty line. Why not read a line with fgets() and strip the newline manually:
char *input(char buf[], size_t size) {
size_t i;
if (!fgets(buf, size, stdin))
return NULL;
for (i = 0; buf[i]; i++) {
if (buf[i] == '\n') {
buf[i] = '\0';
break;
}
}
return buf;
}
In function remove_word, you should define start and end with a larger scope, typically the outer while loop's body. Furthermore s1 should have type char *, not const char *, as the phrase will be modified in place.
You should only increment p and q if the test succeeds and you should check that p and q are not both at the end of their strings.
last but not least: you do not call remove_word in the main function.
The complete code can be simplified into this:
#include <stdio.h>
/* Function to remove a word from a sentence */
char *remove_word(char *s1, const char *s2) {
if (*s2 != '\0') {
char *dst, *src, *p;
const char *q;
dst = src = s1;
while (*src != '\0') {
for (p = src, q = s2; *q != '\0' && *p == *q; p++, q++)
continue;
if (*q == '\0') {
src = p; /* the word was found, skip it */
} else {
*dst++ = *src++; /* otherwise, copy this character */
}
}
*dst = '\0'; /* put the null terminator if the string was shortened */
}
return s1;
}
char *input(char buf[], size_t size) {
size_t i;
if (!fgets(buf, size, stdin))
return NULL;
for (i = 0; buf[i]; i++) {
if (buf[i] == '\n') {
buf[i] = '\0';
break;
}
}
return buf;
}
int main() {
char stringFirst[102];
char stringSecond[22];
printf("Type your text here, up to 100 characters:\n");
if (!input(stringFirst, sizeof stringFirst))
return 1;
printf("\nInsert the word you wish to remove from your text: ");
if (!input(stringSecond, sizeof stringSecond))
return 1;
printf("\nAfter removing the word, the text looks like this now: %s\n",
remove_word(stringFirst, stringSecond));
return 0;
}
Your start and end pointers are defined within a block which makes their scope limited within that block. So, they are not visible to other parts of your code, and if you attempt to reference them outside their scope, the compiler will complain and throw an error. You should declare them at the beginning of the function block.
That said, consider the following approach to delete a word from a string:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int delete_word(char *buf,
const char *word);
int main(void)
{
const char word_to_delete[] = "boy";
fputs("Enter string: ", stdout);
char buf[256];
fgets(buf, sizeof(buf), stdin);
if (delete_word(buf, word_to_delete))
{
printf("Word %s deleted from buf: ", word_to_delete);
puts(buf);
}
else
{
printf("Word %s not found in buf: ", word_to_delete);
puts(buf);
}
system("PAUSE");
return 0;
}
int chDelimit(int ch)
{
return
(ch == '\n' || ch == '\t') ||
(ch >= ' ' && ch <= '/') ||
(ch >= ':' && ch <= '#') ||
(ch >= '[' && ch <= '`') ||
(ch >= '{' && ch <= '~') ||
(ch == '\0');
}
char *find_pattern(char *buf,
const char *pattern)
{
size_t n = 0;
while (*buf)
{
while (buf[n] && pattern[n])
{
if (buf[n] != pattern[n])
{
break;
}
n++;
}
if (!pattern[n])
{
return buf;
}
else if (!*buf)
{
return NULL;
}
n = 0;
buf++;
}
return NULL;
}
char *find_word(char *buf,
const char *word)
{
char *ptr;
size_t wlen;
wlen = strlen(word);
ptr = find_pattern(buf, word);
if (!ptr)
{
return NULL;
}
else if (ptr == buf)
{
if (chDelimit(buf[wlen]))
{
return ptr;
}
}
else
{
if (chDelimit(ptr[-1]) &&
chDelimit(ptr[wlen]))
{
return ptr;
}
}
ptr += wlen;
ptr = find_pattern(ptr, word);
while (ptr)
{
if (chDelimit(ptr[-1]) &&
chDelimit(ptr[wlen]))
{
return ptr;
}
ptr += wlen;
ptr = find_pattern(ptr, word);
}
return NULL;
}
int delete_word(char *buf,
const char *word)
{
size_t n;
size_t wlen;
char *tmp;
char *ptr;
wlen = strlen(word);
ptr = find_word(buf, word);
if (!ptr)
{
return 0;
}
else
{
n = ptr - buf;
tmp = ptr + wlen;
}
ptr = find_word(tmp, word);
while (ptr)
{
while (tmp < ptr)
{
buf[n++] = *tmp++;
}
tmp = ptr + wlen;
ptr = find_word(tmp, word);
}
strcpy(buf + n, tmp);
return 1;
}
If you have to do it manually, just loop over the indicies of your string to find the first one that matches and than you’ll have a second loop that loops for all the others that matches and resets all and jumps to the next index of the first loop if not matched something in order to continue the searching. If I recall accuretaly, all strings in C are accesible just like arrays, you’ll have to figure it out how. Don’t afraid, those principles are easy! C is an easy langugae, thiught very long to write.
In order to remove: store the first part in an array, store the second part in an array, alloc a new space for both of them and concatinate them there.
Thanks, hit the upvote button.
Vitali
EDIT: use \0 to terminate your newly created string.

(char*)malloc(sizeof(char)) causing segmentation fault, how?

The code works fine on most inputs, but for userID's whih are very long I get a segmentation fault. My question is, how can malloc cause a segmentation fault? simply allocating memory shouldn't cause this. I found the problem area by using printf() statements, it seem the malloc within my read_line() function is where the problem is because the second "read_line" does not print, but the first before the malloc does.
thank you.
- Chris
#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 */
#define MEM_OUT printf("%s","out of memory");
/* Defines Node structure. */
struct Node{
char *id;
float weight;
int time;
struct Node *next;
} *head, *p, *t, *last;
/* Constructor which returns a pointer to a new node. */
struct Node * new_node(int time, char *id, float w)
{ /*note malloc returns a pointer */
struct Node *node = (struct Node *)malloc(sizeof(struct Node));
node->time = time;
node->id = (char *)malloc( (strlen(id) + 1) * sizeof(char));
strcpy(node->id, id); //duplicate the id, so new node has own copy.
node->weight = w;
node->next = NULL;
return node;
}
/* reads in line of characters until either a EOF or '\n' is encountered
then places a the terminator '\0' at the end */
char * read_line(FILE *stream)
{
printf("read_line");
char * temp = (char*)malloc(sizeof(char));
printf("read_line");
char * line = (char*)malloc(sizeof(char));
char c;
*line = '\0';
int i = 1;
//strchr()
while( (c = getc(stream)) != EOF && c != '\n')
{
//if(c == EOF) return NULL;
//realloc(line,++i);
strcpy(temp,line);
line = malloc(++i * sizeof(char));
strcpy(line,temp);
temp = malloc(i * sizeof(char));
*(line + (i-1)) = '\0';
*(line + (i-2)) = c;
}
free(temp);
if( i == 1) return NULL;
return line;
}
main() {
int lasttime = 0, timestamp, duration, tokens;
char * userID = NULL;
char * lastuserID = NULL;
char * line = NULL;
float weight,lastweight,change,changePerTime;
head = new_node(0,"",0.0);
last = head;
FILE *fp = fopen("C:\\Users\\chris\\Desktop\\School\\York\\cse\\2031 Software Tools\\Labs\\6\\input.txt","r");
while( (line = read_line(fp)) != '\0') {
printf("%s\n",line);
//free(userID);
line = strtok(line, " \n");
if (line == NULL || sscanf(line,"%d",&timestamp) < 1 || timestamp == 0){
printf("%s\n","Invalid time");
continue;
}
line = strtok(NULL, " \n");
if(line == NULL || isdigit(line[0]) || line[0] == '.') {
printf("Illegal userID");
//free(line);
continue;
}
userID = (char * )malloc( (strlen(line)+1) * sizeof(char));
strcpy(userID,line);
strcat(userID," ");
do{
line = strtok(NULL," \n");
if(line != NULL && !isdigit(line[0]) && line[0] != '.'){
strcat(userID,line ); // adds ' ' and '\0'
strcat(userID," ");
}
}while(line != NULL && line[0] != '.' && !isdigit(line[0]) );
userID[strlen(userID)-1] = '\0'; //erases the tailing space.
if(strlen(userID) > 179){
printf("Illegal userID\n");
printf("mid");
continue;
printf("%s\n","after" );
}
if(line != NULL)
tokens = sscanf(line,"%f", &weight);
if(line == NULL || tokens < 1 || weight < 30.0 || weight > 300.0)
{printf("Illegal weight\n"); continue; }
if (lasttime >= timestamp){
printf("Nonmonotonic timestamps\n");
continue;
}
lasttime = timestamp;
// record is valid apst this point.
/* t = last occurence of this userID, p = last userID*/
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)
{
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");
}
/* add new node to end of list */
last->next = new_node(timestamp,userID,weight);
/* update lastnode */
last = last->next;
free(line);
}
fclose(fp);
/* count sum of id's for last valid user*/
int count=0;
for(p = head->next; p !=NULL; p=p->next)
{
if(strcmp(last->id,p->id) == 0)
count++;
}
//fclose(f); // use if input from file is uncommented
// adds 1 to both demensions to hole axis
int tHeight = 11;
int tWidth = count + 1;
int qHeight = 10;
int qWidth= count;
/* builds the barchart */
char bc[tHeight][tWidth]; // + 1 for y-axis
/* draws axis and orgin */
int a,b;
for(a=0; a<tHeight; a++)
{
for(b=0;b<tWidth; b++)
{
if(a == qHeight && b == 0)
bc[a][b] = '+';
else if(a < tHeight && b == 0)
bc[a][b] = '|';
else if(a == qHeight && b > 0)
bc[a][b] = '-';
}
}
/* prints the bars */
int j=1, i, k, bh;
for(p = head; p != NULL, j < tWidth; p=p->next)
{
if(strcmp(last->id,p->id) == 0)
{
for(i = 9, k=0, bh = (int)(p->weight / 30);i >= 0; i--)
{
if(k < bh)
{
bc[i][j] = '*';
k++; // barheight
}
else
bc[i][j] = ' ';
}
j++;
}
}
/* prints the barchart */
int m, n;
for(m=0; m < tHeight; m++)
{
for(n=0; n < tWidth; n++)
{
printf("%c",bc[m][n]);
}
printf("%c",'\n');
}
}
The malloc calls are not causing a segmentation fault. But your use of them later on could be.
Some Items of Note
Your printf("read line") statements will not print out immediately when called because the output is buffered. If you want them to print right away, do printf("read line\n"). You'll then see that both execute and your code that uses the tiny buffer you allocated will cause the crash.
In your while loop, you are doing more malloc calls and assigning the returns to variables, like temp and line, without freeing the prior memory pointers that temp and line hold, thus causing some memory leaks. Your commented out realloc was the better thought process: line = realloc(line, ++i * sizeof(*line));. Similarly for temp.
Memory Allocation Problem
One very problematic area is here:
userID = (char * )malloc( (strlen(line)+1) * sizeof(char));
strcpy(userID,line);
strcat(userID," ");
userID can hold the length of the string in line (strlen(line)) plus one more byte. But that one more byte is needed for the null terminator. Your strcat(userID, " ") will write past the length of the allocated buffer for userID by one byte.
I solved the problem! The issue wasn't the read_line function at all, it was the memory allocation for the userID string. Moving the malloc() for the user to the beginning of the loop fixed the problem.
The amount of memory allocated for the userID portion of the line was based on the the length of the whole line. eg: malloc(strlen(line)+2). However this was done after calling strtok() on the line a few times, which would allocate a memory block shorter than the length of the entire line. This is because strtok() places null terminators '\0' 's at every instance of the specified delimiter in the line, and strlen() only counts the length from the passed character pointer to the first '\0' it encounters.
Anyway, thanks for your help guys!
-Chris
#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 */
/* Defines Node structure. */
struct Node{
char *id;
float weight;
int time;
struct Node *next;
} *head, *p, *t, *last;
/* Constructor which returns a pointer to a new node. */
struct Node * new_node(int time, char *id, float w)
{ /*note malloc returns a pointer */
struct Node *node = (struct Node *)malloc(sizeof(struct Node));
node->time = time;
node->id = malloc(strlen(id) + 1);
strcpy(node->id, id); //duplicate the id, so new node has own copy.
node->weight = w;
node->next = NULL;
return node;
}
/* reads in line of characters until either a EOF or '\n' is encountered
then places a the terminator '\0' at the end */
char * read_line(FILE *in)
{
int i = 1;
char * s = NULL;
char c;
do{
s = realloc(s,i); //strlen does not work on NULL strings
if(s == NULL || s == "")
{
printf("%s\n","out of memory");
exit(1);
}
*(s + (i-1)) = '\0'; // ensures null terminated
if(i > 1)
*(s + (i-2)) = c;
i++;
}
while( (c = getc(in)) != EOF && c != '\n' );
if (c == '\n')
return s;
else if(c == EOF)
return NULL;
}
main() {
int lasttime = 0, timestamp, duration, tokens;
char * userID = NULL;
char * lastuserID = NULL;
char * line = NULL;
float weight,lastweight,change,changePerTime;
head = new_node(0,"",0.0);
last = head;
FILE *fp = fopen("C:\\Users\\chris\\Desktop\\School\\York\\cse\\2031 Software Tools\\Labs\\6\\tests\\04.in","r");
while((line = read_line(fp)) != NULL) {
userID = malloc(strlen(line)+2); // max userID length is line length**
line = strtok(line, " \n");
if (line == NULL || sscanf(line,"%d",&timestamp) < 1 || timestamp == 0){
printf("%s\n","Invalid time");
continue;
}
line = strtok(NULL, " \n");
if(line == NULL || isdigit(line[0]) || line[0] == '.') {
printf("%s\n","Illegal userID");
//free(line);
continue;
}
strcpy(userID,line);
strcat(userID," ");
do{
line = strtok(NULL," \n");
if(line != NULL && !isdigit(line[0]) && line[0] != '.'){
strcat(userID,line ); // adds ' ' and '\0'
strcat(userID," ");
}
}while(line != NULL && line[0] != '.' && !isdigit(line[0]) );
userID[strlen(userID)-1] = '\0'; //erases the tailing space.
if(strlen(userID) > 179){
printf("Illegal userID\n");
free(userID);
free(line);
continue;
}
if(line != NULL)
tokens = sscanf(line,"%f", &weight);
if(line == NULL || tokens < 1 || weight < 30.0 || weight > 300.0)
{printf("Illegal weight\n"); continue; }
if (lasttime >= timestamp){
printf("Nonmonotonic timestamps\n");
continue;
}
lasttime = timestamp;
// record is valid apst this point.
/* t = last occurence of this userID, p = last userID*/
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)
{
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");
}
/* add new node to end of list */
last->next = new_node(timestamp,userID,weight);
/* update lastnode */
last = last->next;
free(line);
} // end of input loop
fclose(fp);
/* count sum of id's for last valid user*/
int count=0;
for(p = head->next; p !=NULL; p=p->next)
{
if(strcmp(last->id,p->id) == 0)
count++;
}
//fclose(f); // use if input from file is uncommented
// adds 1 to both demensions to hole axis
int tHeight = 11;
int tWidth = count + 1;
int qHeight = 10;
int qWidth= count;
/* builds the barchart */
char bc[tHeight][tWidth]; // + 1 for y-axis
/* draws axis and orgin */
int a,b;
for(a=0; a<tHeight; a++)
{
for(b=0;b<tWidth; b++)
{
if(a == qHeight && b == 0)
bc[a][b] = '+';
else if(a < tHeight && b == 0)
bc[a][b] = '|';
else if(a == qHeight && b > 0)
bc[a][b] = '-';
}
}
/* prints the bars */
int j=1, i, k, bh;
for(p = head; p != NULL, j < tWidth; p=p->next)
{
if(strcmp(last->id,p->id) == 0)
{
for(i = 9, k=0, bh = (int)(p->weight / 30);i >= 0; i--)
{
if(k < bh)
{
bc[i][j] = '*';
k++; // barheight
}
else
bc[i][j] = ' ';
}
j++;
}
}
/* prints the barchart */
int m, n;
for(m=0; m < tHeight; m++)
{
for(n=0; n < tWidth; n++)
{
printf("%c",bc[m][n]);
}
printf("%c",'\n');
}
}

How do I read and store string of arbitrary length using malloc and realloc in C?

I have a structure
typedef struct store
{
char name[11];
int age;
} store;
and a main function(below is part of it):
int main()
{
int i=0;
int inputs;
char line[100];
char name[11];
char command[11];
store read[3000];
while(i < 3000 && gets(line) != NULL)
{
int tempage;
inputs = sscanf(line, "%10s %10s %d", command, name, &tempage);
if (inputs == 3)
{
if (strcmp(command, "register") == 0)
{
strncpy(read[i].name, name,10);
read[i].age = tempage;
i++;
....
I need to modify it so that it can read a line of arbitrary length, and store the name from the line which is also a string of arbitrary length using malloc and realloc.
How should I approach this?
What you need to do is read the line in smaller increments, and resize your buffer as you go.
As an example (not tested and not meaning to be particularly elegant, just an example):
char *readline(FILE *f)
{
char *buf = NULL;
size_t bufsz = 0, len = 0;
int keep_going = 1;
while (keep_going)
{
int c = fgetc(f);
if (c == EOF || c == '\n')
{
c = 0; // we'll add zero terminator
keep_going = 0; // and terminate the loop afterwards
}
if (bufsz == len)
{
// time to resize the buffer.
//
void *newbuf = NULL;
if (!buf)
{
bufsz = 512; // some arbitrary starting size.
newbuf = malloc(bufsz);
}
else
{
bufsz *= 2; // issue - ideally you'd check for overflow here.
newbuf = realloc(buf, bufsz);
}
if (!newbuf)
{
// Allocation failure. Free old buffer (if any) and bail.
//
free(buf);
buf = NULL;
break;
}
buf = newbuf;
}
buf[len++] = c;
}
return buf;
}
Change the name[11] to *name;
Allocate memory for that everytime using malloc.
By the way, register is a keyword in C language. You can't use it like you did !
I think what you're looking for is:
char* name;
name = (char*)malloc(sizeof(char));
This alternative approach is similar to #asveikau's, but economize on the use of malloc() by copying on the stack.
Please do not use this for homework answer.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
char * slurponeline(FILE *f, int s) {
const int size = 4096;
char buffer[size];
char * r;
int c,i=0;
while( i<size && (c = fgetc(f),c>=0 && c!='\n')) buffer[i++]=c;
if (0 == s && 0 == i) return 0;
r = (size==i)? slurponeline(f,s+size):malloc(s+i);
memcpy(r+s,buffer,i);
return r;
}
int main(int argc, char ** argv) {
FILE * f = fopen(argc>1?argv[1]:"a.out","rb");
char * a,*command,*commandend,*name,*nameend;
int age;
while (a = slurponeline(f,0)) {
char * p = a;
while (*p && *p == ' ') ++p; // skip blanks.
command = p;
while (*p && *p != ' ') ++p; // skip non-blanks.
commandend = p;
while (*p && *p == ' ') ++p; // skip blanks.
name = p;
while (*p && *p != ' ') ++p; // skip non-blanks.
nameend = p;
while (*p && *p == ' ') ++p; // skip blanks.
age = atoi(p);
*commandend=0;
*nameend=0;
printf("command: %s, name: %s, age: %d\n",command,name,age);
free(a);
}
}

Resources