My linked list is printing the last word in my text file for all the elements - c

This code is reading a text file and inserting each word into the linked list.
I am new to linked lists and have been working on this for four hours now and cannot for the life of me figure this out.
So what is going on here? I have checked every way I know how to, and for the life of me cannot get the linked list to print correctly. I believe it has something to do with the push/append functions. Somehow it is overwriting everything previously in the linked list. Maybe the printlist function is overwriting everything but I don't see how it could possibly be doing that.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
// A complete working C program to demonstrate all insertion methods
// on Linked List
// A linked list node
struct Node;
void push(struct Node **head_ref, char *new_data);
void insertAfter(struct Node *prev_node, char *new_data);
void append(struct Node **head_ref, char *new_data);
void printList(struct Node *node);
int LINECOUNT(FILE *(*stream), char *filename);
struct Node {
char *data;
struct Node *next;
};
/* Given a reference (pointer to pointer) to the head of a list and
an int, inserts a new node on the front of the list. */
void push(struct Node **head_ref, char *new_data) {
/* 1. allocate node */
struct Node* new_node = (struct Node *)malloc(sizeof(struct Node));
/* 2. put in the data */
new_node->data = new_data;
printf("push data:%s ", new_data);
/* 3. Make next of new node as head */
new_node->next = (*head_ref);
/* 4. move the head to point to the new node */
(*head_ref) = new_node;
}
/* Given a reference (pointer to pointer) to the head
of a list and an int, appends a new node at the end */
void append(struct Node **head_ref, char *new_data) {
/* 1. allocate node */
struct Node* new_node = (struct Node *)malloc(sizeof(struct Node));
struct Node *last = *head_ref; /* used in step 5*/
/* 2. put in the data */
new_node->data = new_data;
printf("push data:%s ", new_data);
/* 3. This new node is going to be the last node, so make next of
it as NULL*/
new_node->next = NULL;
/* 4. If the Linked List is empty, then make the new node as head */
if (*head_ref == NULL) {
*head_ref = new_node;
return;
}
/* 5. Else traverse till the last node */
while (last->next != NULL)
last = last->next;
/* 6. Change the next of last node */
last->next = new_node;
return;
}
// This function prints contents of linked list starting from head
void printList(struct Node *node) {
while (node != NULL) {
printf(" %s ", node->data);
node = node->next;
}
}
int LINECOUNT(FILE *(*stream), char *filename) {
int size = 0;
size_t chrCount;
char *text;
if ((*stream = fopen(filename, "r")) == NULL) {
printf("LC Could not open hw8 data file.\n");
exit(0);
}
while (1) {
text = NULL;
getline(&text, &chrCount, *stream);
free(text); /*free text*/
if (feof(*stream))
break;
size++;
}
rewind(*stream);
return size;
}
/*int wordCount(FILE *(*stream), char *filename, int lineCount) {
char ch;
int wordcount = 0;
int charcount = 0;
*stream = fopen(filename, "r");
int x = 0;
int lineArr[lineCount];
for (int i = 0; i < lineCount; i++) {
lineArr[i] = 0;
}
if (*stream) {
while ((ch = getc(*stream)) != EOF) {
if (ch != ' ' && ch != '\n') {
charcount++;
}
if (ch == ' ' || ch == '\n') {
wordcount++;
lineArr[x] = lineArr[x] + 1;
}
if (ch == '\n') {
x++;
}
}
if (charcount > 0) {
wordcount++;
charcount++;
}
} else {
printf("Failed to open the file\n");
}
// rewind(*stream);
return lineArr;
}*/
int main(void) {
char *fn = "hw8data.txt";
int lineCount;
FILE *stream;
lineCount = LINECOUNT(&stream, fn);
//int lineArr[lineCount];
//int lineArr[];//lineArr[0] = 4 would say the first line has 4 words. using this data for strtok
//lineArr = wordCount(&stream, fn, lineCount);
//-------------------------------------
char ch;
int wordcount = 0;
int charcount = 0;
stream = fopen("./hw8data.txt", "r");
int x = 0;
int lineArr[lineCount];
for (int i = 0; i < lineCount; i++) {
lineArr[i] = 0;
}
if (stream) {
while ((ch = getc(stream)) != EOF) {
if (ch != ' ' && ch != '\n') {
charcount++;
}
if (ch == ' ' || ch == '\n') {
wordcount++;
lineArr[x] = lineArr[x] + 1;
}
if (ch == '\n') {
x++;
}
}
//if (charcount > 0) { wordcount++; charcount++; }
} else {
printf("Failed to open the file\n");
}
/* Start with the empty list */
struct Node *head = NULL;
rewind(stream);
char *sArr = malloc(42 * sizeof(char));
fscanf(stream, "%s ", sArr);
printf("%s ", sArr);
push(&head, sArr);
fscanf(stream, "%s ", sArr);
printf("%s ",sArr);
append(&head, sArr);
printList(head);
return 0;
}

char* sArr=malloc(42*sizeof(char));
fscanf(stream,"%s ",sArr);
printf("%s ",sArr);
push(&head,sArr);
fscanf(stream,"%s ",sArr);
printf("%s ",sArr);
append(&head,sArr);
You add the same value to the list twice, the value you got back from your one and only call to malloc. If you want two nodes to hold different values, don't add the same value twice. One ugly fix is if after push(&head,sArr) you add another sArr = malloc(42*sizeof(char));. That way, your call to append will add a different value to the list.
If you don't see this, add code to output the value of node->data as you print the list. You'll see that both nodes have pointers to the same chunk of memory, the value you got back from that call to malloc.
But it would be much more elegant if your list entries owned their contents. That would require functions like push and append to allocate their own pointers, copy the strings into them, and use those new pointers. Your code to destroy a list could call free on the data pointed to as well as the nodes.

I would suggest a completely different approach.
I would use a C99 flexible array member for storing each word. Also, because I don't want my code to be submittable as a homework answer, I'll show how to do it with wide-character input. (On basically all OSes except possibly Windows, it treats non-ASCII characters like Ö and Ø as letters, if your locale says they are.)
struct word {
struct word *next;
wchar_t data[]; /* Flexible array member */
};
I would use a helper function that reads the next word from a wide stream, skipping any non-word characters (which I assume to be alphanumeric characters, i.e. letters and digits):
struct word *wide_word(FILE *input)
{
struct word *w = NULL, *tempw;
size_t max = 0; /* No characters allocated in w yet */
size_t len = 0; /* No characters in w yet */
wint_t c;
/* NULL input is not allowed. */
if (!input) {
errno = EINVAL;
return NULL;
}
/* Also fail if the stream is already in an error state. */
if (ferror(input)) {
errno = EIO;
return NULL;
}
c = getwc(input);
/* Skip leading non-word characters. */
while (c != WEOF && !iswalnum(c))
c = getwc(input);
/* End of input? */
if (c == WEOF) {
errno = 0;
return NULL;
}
/* Append each wide word character. */
while (c != WEOF && iswalnum(c)) {
/* Need to reallocate? */
if (len >= max) {
/* Since words are usually short, let's allocate
in chunks of 64 wide characters. */
max = (len | 63) + 65;
tempw = realloc(w, sizeof (struct word) + max * sizeof (wchar_t));
if (!tempw) {
/* Out of memory. */
free(w);
errno = ENOMEM;
return NULL;
}
w = tempw;
}
/* Append. */
w->data[len++] = c;
c = getwc(input);
}
/* Although not useful for this particular case,
we normally want to keep the separator intact. */
if (c != WEOF)
ungetwc(c, input);
/* Optimize the memory allocated to the word. */
if (max != len + 1) {
max = len + 1;
tempw = realloc(w, sizeof (struct word) + max * sizeof (wchar_t));
if (!tempw) {
free(w);
errno = ENOMEM;
return NULL;
}
w = tempw;
}
/* Terminate the wide string in w. */
w->data[len] = L'\0';
/* Success! */
return w;
}
I personally prefer to prepend new nodes to the list, then reverse the entire list afterwards:
struct word *reverse_list(struct word *oldlist)
{
struct word *newlist = NULL;
struct word *w;
while (oldlist) {
w = oldlist;
oldlist = oldlist->next;
w->next = newlist;
newlist = w;
}
return newlist;
}
With the above, a program to read wide words from standard input is basically
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <locale.h>
#include <stdio.h>
#include <wchar.h>
#include <errno.h>
/* Functions listed above */
int main(void)
{
struct word *list, *node;
if (!setlocale(LC_ALL, ""))
fprintf(stderr, "Warning: Your C library does not support your current locale.\n");
if (fwide(stdin, 1) < 1)
fprintf(stderr, "Warning: Your C library does not support wide standard input.\n");
if (fwide(stdout, 1) < 1)
fprintf(stderr, "Warning: Your C library does not support wide standard output.\n");
/* Read words from standard input into reversed list. */
while (1) {
node = wide_word(stdin);
if (!node) {
if (errno) {
fprintf(stderr, "Error reading standard input: %s.\n", strerror(errno));
exit(EXIT_FAILURE);
}
/* No error, just end of input. */
break;
}
/* Prepend to list. */
node->next = list;
list = node;
}
/* Reverse the list so first word is first in list. */
list = reverse_list(list);
/* Print each word in the list to standard output, in order. */
for (node = list; node != NULL; node = node->next)
wprintf(L"%ls\n", node->data);
/* We could free() each word in 'list' here. */
return EXIT_SUCCESS;
}

Related

Alphabetize and word frequency from a file using strtok in C

My goal is to analyze a text file, tokenize each word, then alphabetize each word with its word frequency.
Example:
Input: The house is on the ground on earth.
Output:
earth - 1
ground - 1
house - 1
is - 1
on - 2
the - 2
I have been able to open the file, read the file line by line, tokenize each word, converted the tokens to lowercase. I am stuck grouping and alphabetizing each token.
#include <stdio.h>
#include <stdlib.h>
void lower_string(char s[]);
int main()
{
FILE *file;
//char path[100];
char ch[100];
int characters;
/* Input path of files to merge to third file
printf("Enter source file path: ");
scanf("%s", path);
file = fopen(path, "r");*/
file = fopen("test.txt", "r"); //testing w.o repeated input
/* Check if file opened successfully */
if (file == NULL)
{
printf("\nUnable to open file.\n");
printf("Please check if file exists and you have read privilege.\n");
exit(EXIT_FAILURE);
}
const char delim[] = " ,.;!?[\n]";
char *token;
int tokenNum;
while (fgets(ch, sizeof(ch), file) != NULL)
{
lower_string(ch);
token = strtok(ch, delim);
while (token != NULL)
{
printf("Token:%s\n", token);
token = strtok(NULL, delim);
tokenNum++;
}
}
printf("%d\n", tokenNum); //total words testing
/* Close files to release resources */
fclose(file);
return 0;
}
void lower_string(char s[])
{
int c = 0;
while (s[c] != '\0')
{
if (s[c] >= 'A' && s[c] <= 'Z')
{
s[c] = s[c] + 32;
}
c++;
}
}
I have been looking into building and manipulating an ordered linked list of integers and binary search tree of integers. I'm having a hard time figuring out where I should begin to implement these features. So far i have been looking at the code below for ordered linked list.
#include <stdio.h>
#include <stdlib.h>
//These structures are declared globally so they are available to all functions
//in the program.
typedef struct list_node_s
{ //defines structure of one node
int key; //key value - here an integer
int count; //frequency key value encountered in input
struct list_node_s *restp; //pointer to the next node in list = NULL if EOL
} list_node_t;
typedef struct //defines head of list structure
{
list_node_t *headp; //pointer to first node in list, NULL if list is empty
int size; //current number of nodes in the list
} ordered_list_t;
//Prototypes
list_node_t * insert_in_order (list_node_t * old_listp, int new_key);
void insert (ordered_list_t * listp, int key);
int delete (ordered_list_t * listp, int target);
list_node_t * delete_ordered_node (list_node_t * listp, int target,int *is_deleted);
void print_list (ordered_list_t * listp);
#define SEND -999 //end of input sentinal
int main (void)
{
int next_key;
ordered_list_t my_list = {NULL, 0};
printf("\n\nProgram to build, display and manipulate (delete) an Ordered Linked List \n");
printf("\nAdapted from code in \"Problem Solving and Programming in C\" by J.R. Hanly and E.B. Koffman\n\n");
printf ("enter integer keys - end list with %d\n", SEND);
/* build list by in-order insertions*/
for (scanf ("%d", &next_key);
next_key != SEND;
scanf ("%d", &next_key))
{
insert (&my_list, next_key);
}
/* Display completed list */
printf ("\nOrdered list as built:\n");
print_list(&my_list);
/* Process requested deletions */
printf("enter key value for node to be removed from list or %d to end > ", SEND);
for (scanf ("%d", &next_key);
next_key != SEND;
scanf ("%d", &next_key))
{
if (delete (&my_list, next_key))
{
printf ("%d deleted.\n New list:\n", next_key);
print_list (&my_list);
}
else
{
printf ("No deletion. %d not found\n", next_key);
}
printf ("enter key value for node to be removed from list or %d to end > ", SEND);
}
return (0);
}
/* prints contents of a linked list Display the elements in the list pointed to by the pointer list.*/
void print_list (ordered_list_t * listp)
{
list_node_t * tmp;
for (tmp = listp->headp; tmp != NULL; tmp = tmp->restp)
printf ("key = %d; count = %d\n", tmp->key, tmp->count);
printf ("\n\n");
}
//Inserts a new node containing new_key into an existing list and returns a pointer to the first node of the new list
list_node_t * insert_in_order (list_node_t * old_listp, int new_key)
{
list_node_t * new_listp;
if (old_listp == NULL) //check for end of list (EOL)
{
new_listp = (list_node_t *) malloc (sizeof (list_node_t));
new_listp->key = new_key;
new_listp->count = 1;
new_listp->restp = NULL;
}
else if (old_listp->key == new_key) //check for matching key, increment count
{
old_listp->count++;
new_listp = old_listp;
}
else if (old_listp->key > new_key) //Next node key value > new key, so insert new node at current location
{
new_listp = (list_node_t *) malloc (sizeof (list_node_t));
new_listp->key = new_key;
new_listp->count = 1;
new_listp->restp = old_listp;
}
else
{
new_listp = old_listp;
new_listp->restp = insert_in_order (old_listp->restp, new_key);
}
return (new_listp);
}
//inserts a node into an ordered list_node_t
void insert (ordered_list_t * listp, int key)
{
++(listp->size);
listp->headp = insert_in_order (listp->headp, key);
}
//deletes the first node containing the target key from an ordered list; returns 1
//if target found & deleted, 0 otherwise (means target not in list)
int delete (ordered_list_t * listp, int target)
{
int is_deleted;
listp->headp = delete_ordered_node (listp->headp, target, &is_deleted);
if (is_deleted)
--(listp->size); //reduce current node count (size); keep size of list current
return (is_deleted);
}
/* deletes node containing target key from a list whose head is listp; returns a pointer
to the modified list (incase it is the first node, pointed to by listp), frees
the memory used by tyhe deleted node and sets a flag to indicate success (1) or
failure (0; usually means no such node found).
*/
list_node_t * delete_ordered_node (list_node_t * listp, int target, int *is_deleted)
{
list_node_t *to_freep, *ansp;
// if list empty, nothing to do; return NULL
printf ("check for empty list; target: %d \n", target);
if (listp == NULL)
{
*is_deleted = 0;
ansp = NULL;
}
//if first node is to be deleted, do it; relink rest of list to list header struct
else if (listp->key == target)
{
printf ("at first node; target: %d \n", target);
*is_deleted = 1;
to_freep = listp; //keeps track of node memory location to be freed
ansp = listp->restp;
free (to_freep); //release the memory of the deleted node for reuse
}
//if target exists, it is further down the list (recursive step), make recursive call
//to move down the list looking for the target value
else
{
printf ("chase down list to find: %d \n", target);
ansp = listp;
ansp->restp = delete_ordered_node (listp->restp, target, is_deleted);
}
return (ansp);
}
I'm finding it hard to implement that with strtok.
12/4 EDIT:
added: Nodes for BST.
Questions-
Don't know if key needs to be tracked.(I assume it'll be useful to pull specific words).
Where/how would I add the logic to alphabetize the tree.(study sources appreciated)
How do I pass each word through this tree?
#define WLENGTH 100
//Base Node info
struct node
{
char word[WLENGTH];
int key;
int freq;
struct node *left, *right;
};
//Function to create a new node
struct node *newNode(char wordn, int item, int freqn)
{
struct node *temp = (struct node *) malloc(sizeof(struct node));
temp->word = wordn;
temp->key = item;
temp->freq = freqn;
temp->left = temp->right = NULL;
return temp;
}
//Function to place nodes in order
void inorder(struct node *root)
{
if (root != NULL)
{
inorder(root->left);
printf("%d ", root->key);
inorder(root->right);
}
}
/*Function to insert a new node with given key*/
struct node* insert(struct node* node, int key)
{
/* If the tree is empty, return a new node */
if (node == NULL)
return newNode(key);
/* Otherwise, recur down the tree */
if (key < node->key)
node->left = insert(node->left, key);
else if (key > node->key)
node->right = insert(node->right, key);
/* return the (unchanged) node pointer */
return node;
}
At the request of the OP, here is a bit of code to bulk load an entire text file for processing:
FILE *mustOpen( char *fname, char *mode ) {
FILE *fp = fopen( fname, mode );
if( fp == NULL ) {
fprintf( stderr, "Cannot open '%s'\n", fname );
exit( EXIT_FAILURE );
}
return fp;
}
// Passed the path to a file, opens, measures and bulk loads entire file (plus terminating '\0')
char *loadFile( char *fname ) {
FILE *fp = mustOpen( fname, "rb" );
fseek( fp, 0, SEEK_END );
size_t size = ftell( fp );
fseek( fp, 0, SEEK_SET );
char *buf;
if( ( buf = malloc( size + 1) ) == NULL )
fprintf( stderr, "Malloc() failed\n" ), exit( EXIT_FAILURE );
if( fread( buf, sizeof *buf, size, fp ) != size )
fprintf( stderr, "Read incomplete\n" ), exit( EXIT_FAILURE );
fclose( fp );
*(buf + size) = '\0'; // xtra byte allows strXXX() to work
return buf; // pointer to heap allocated buffer containing file's bytes
}
Remember to free() the buffer when done with its contents.
With the entire text loaded (and NULL terminated), here is a way to skip along the entire "string" finding each "word" (as defined by the delimiters):
for( char *cp = buf; (cp = strtok( cp, delim )) != NULL; cp = NULL ) {
/* process each single "word" */
}
Since the "text" is in memory, instances of each of the "words" are in memory, too. All that's needed is populating a BST with nodes that 'point to' one instance of each "word" and a counter that counts multiple occurrences of each word.
Finally, an "in order" traversal of the BST will give an alphabetised list of words and their frequency in the text.
Be sure to compartmentalize each of the functions. The "blocks" of functionality can then be re-used in other projects, and, who knows?... You may want to first load a dictionary and only report the words (and locations) that do not appear in the dictionary (typos?). The code that handles the BST "structure" (searching, adding, traversing) should be somewhat independent of what "information fields" comprise each node.

Creating a singly linked list from a .txt file and reversing odd numbers of each line in C

I have a project about linked lists but I'm having a hard time doing it. The teacher wants me to read a .txt file and create singly linked list from it. After that, I need to reverse odd numbers of every line. Then print it. Here is the code which I used for printing the linked list. But I need help to reverse the odd numbers of each line.
This is the code which I used to print the list:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct list {
char *string;
struct list *next;
};
typedef struct list LIST;
int main(void) {
FILE *fp;
char line[10];
LIST *current, *head;
head = current = NULL;
fp = fopen("data.txt", "r");
while(fgets(line, sizeof(line), fp)){
LIST *node = malloc(sizeof(LIST));
node->string = strdup(line);
node->next =NULL;
if(head == NULL){
current = head = node;
} else {
current = current->next = node;
}
}
fclose(fp);
for(current = head; current ; current=current->next){
printf("%s", current->string);
}
return 0;
}
Here is the content of the .txt file:
10
9,6,11,7,12,18,19,14,15,13
13,14,9,12,15,3,18,20,1,2
4,11,8,17,12,15,20,10,3,16
19,4,11,1,13,17,12,16,20,18
1,6,20,11,13,9,7,16,10,2
12,4,11,16,3,20,9,19,17,15
20,3,10,12,18,2,5,14,15,16
18,19,15,2,6,9,1,3,17,4
7,6,20,1,11,4,3,5,8,16
1,2,16,13,17,10,12,9,4,15
"But I need help to reverse the odd numbers of each line."
There are several other parts that need to be considered before this step can be developed.
Following are suggestions for a functions approach implementation using your problem description. A few items are simply suggestions to simplify the existing code. And a few other steps, are not mentioned as necessary, but should be considered:
Since you are not mandated to use char *string; in your problem description, choose to use a reasonable string length variable that does not require an additional layer of dynamic allocation, such as char string[260]; (or even smaller to fit your input file.) This will greatly simplify the code.
Because the input file is sized with lines ~30 char long, declare the variable line to be at least large enough to contain one line, eg 80 would allow larger values, and still allow enough space, but since memory is cheap, go with the same size as is used in the string member of your linked list.
Move the work of populating each new node to a function. It also will greatly simplify the program, and provide greater readability. Eg: void insert(LIST **head_ref, char *str);
Always test the return of fopen() before attempting to use the file descriptor.
To manipulate the contents of each odd row (eg 1, 3, 5, 7, 9), as numbers, the contents of each line read in from a file as a string, needs to first be converted to a collection of numbers. This suggests an additional member be added to the struct. For example int num[10].
The previous observation implicitly suggests the need of an additional function to parse and convert each comma delimited string into discrete integer values. Perhaps with the prototype: void parseIntArray(LIST **list);
The next and final task also suggests an additional function to reverse the contents of selected array member integer arrays. This one might use a prototype such as: void reverse_odd(LIST **list, size_t size);
Finally, because each node of LIST created required dynamically allocated memory, once finished using LIST, the memory must be given back to the OS to prevent memory leaks. An additional function to do this could be prototyped: void freeList(LIST **head);
Following are the main() function and preceding support declarations etc. It is intended here to illustrate the above suggested steps, and the benefits of breaking down a bigger problem into smaller problems, then implementing each smaller solution to support the whole. Benefits include for example readability and maintainability and potential re-use of code-base, (Note the similarity of argument lists in each supporting function.):
#define MAX_STRLEN 260 //use mnemonic values to avoid magic numbers in code
struct list {
char string[MAX_STRLEN];
int arr[10];
struct list *next;
};
typedef struct list LIST;
//Prototypes of 'smaller' solutions
void insert(LIST **head_ref, char *str);
void parseIntArray(LIST **list);
void reverse_odd(LIST **list, size_t size);
void freeList(LIST **head);
int main(void)
{
FILE *fp;
char line[MAX_STRLEN];
LIST *current, *head;
char *convPtr = NULL;
head = current = NULL;
fp = fopen("data.txt", "r");
if(fp)
{
//consume 1st line
if(fgets(line, sizeof(line), fp));//10
{
sizeArray = strtol(line, &convPtr, 10);
if(errno != ERANGE)
{
while(fgets(line, sizeof(line), fp))
{
//(see implementations of each below)
//create new node, insert num string
insert(&current, line);
//convert new->string to integers, place in new->array
parseIntArray(&current);
//reverse 'odd' contents of each array
reverse_odd(&current, sizeArray);
}
}else{//handle error and leave}
}
fclose(fp);
}else{//handle error and leave}
//At this point in code, entire file is captured into nodes of list.
//use list as needed
//When finished using list, memory must be freed to prevent memory leaks
head = current;
freeList(&head);
return 0;
}
The remaining code segments are the function implementations used above:
void freeList(LIST **head)
{
LIST *tmp;
while (*head != NULL)
{
tmp = (*head);
(*head) = (*head)->next;
free(tmp);
}
}
//create new node, insert num string
void insert(LIST **head_ref, char *str)
{
int *arr = malloc(numNodes * sizeof(*arr));
//allocate node
LIST* new = calloc(1, sizeof(*new));
//put in the data
strcpy(new->string, str);
//Make next of new node as head
new->next = (*head_ref);
//Move the head to point to the new node
(*head_ref) = new;
}
//convert new->string to integers, place in list->array
void parseIntArray(LIST **list)
{
char *tok = NULL;
int i = 0;
int tmp = 0;
char *sArray = strdup((*list)->string);
tok = strtok(sArray, ",\n ");
while(tok)
{
errno = 0;
tmp = atoi(tok);
if(errno == ERANGE)
{
printf("Error converting string to number\nExiting.");
return;
}
(*list)->arr[i] = tmp;
i++;
tok = strtok(NULL, ",\n ");
}
}
//reverse 'odd' contents of list->array
void reverse_odd(LIST **list, size_t size)
{
int *ptr = &((*list)->arr[0]);
int *tmp = malloc(size * sizeof(*tmp));
memset(tmp, -1, size*sizeof(*tmp));
for(int i=0;i<size;i++)
{
if(ptr[i]%2 != 0)
tmp[size-1-i] = ptr[i];
}
for(int i=0;i<size;i++)
{
if(tmp[i] < 0)
{
while((*ptr)%2 != 0 ) ptr++;
tmp[i] = *ptr;
ptr++;
}
}
memcpy((*list)->arr, tmp, size*sizeof(int));
}
This hope this code will do the job.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct line {
struct num *first;
struct line *next;
} LineNode;
typedef struct num {
int num;
int order;
struct num *next;
} NumNode;
int main() {
FILE *fp;
char ch;
int counter = 0;
NumNode *curr_num, *even_ptr, *odd_ptr, *odd_head, *even_head;
LineNode *curr_line, *line_head;
curr_num = even_head = odd_head = even_ptr = odd_ptr = NULL;
line_head = curr_line = NULL;
fp = fopen("data.txt", "r");
if (fp == NULL)
{
return 1;
}
ch = fgetc(fp);
while(ch != EOF){
if (ch >= 48 && ch <= 57)
{
int n = 0;
while (ch != EOF && ch != '\n' && ch >= 48 && ch <= 57)
{
int x = ch - 48;
n = n * 10 + x;
ch = fgetc(fp);
}
NumNode *node = malloc(sizeof(NumNode));
node->num = n;
node->order = counter;
node->next =NULL;
if (n % 2 == 0){
if(even_head == NULL){
even_head = even_ptr = node;
} else {
even_ptr = even_ptr->next = node;
}
}else{
if(odd_head == NULL){
odd_head = node;
} else {
node->next = odd_head;
odd_head = node;
}
}
counter++;
}
if (ch == '\n' || ch == EOF)
{
NumNode *num_node, *head;
num_node = head = NULL;
even_ptr = even_head;
odd_ptr = odd_head;
counter = 0;
if (even_head != NULL && even_head->order == counter){
head = num_node = even_ptr;
even_ptr = even_ptr->next;
} else {
head = num_node = odd_ptr;
odd_ptr = odd_ptr->next;
}
counter++;
while (even_ptr != NULL)
{
if (even_ptr->order == counter) {
num_node = num_node->next = even_ptr;
even_ptr = even_ptr->next;
}
else if (odd_ptr != NULL) {
num_node = num_node->next = odd_ptr;
odd_ptr = odd_ptr->next;
}
counter++;
}
while (odd_ptr != NULL)
{
num_node = num_node->next = odd_ptr;
odd_ptr = odd_ptr->next;
}
LineNode *node = malloc(sizeof(LineNode));
node->next =NULL;
node->first = head;
if (line_head == NULL)
line_head = curr_line = node;
else
curr_line = curr_line->next = node;
odd_head = even_head = NULL;
counter = 0;
}
ch = fgetc(fp);
}
fclose(fp);
for(curr_line = line_head; curr_line != NULL ; curr_line=curr_line->next) {
for(curr_num = curr_line->first; curr_num != NULL ; curr_num=curr_num->next) {
printf("%d", curr_num->num);
if (curr_num->next != NULL)
printf(",");
}
printf("\n");
}
return 0;
}

Read a file line by line and scan each character seperated by comma into seperate variables in C

Have to read file.txt with format as mentioned below:
12AA,abc12 \n
4CCC,cde15 \n
I want to read the file line by line and store comma separated values in separate variables.
fp = fopen("file.txt", "r");
while(fgets(buffer, 255, (FILE*) fp))
{
fscanf(fp, "%s %s", acc_no, package);
printf("%s\n", acc_no);
printf("%s\n", package);
}
fclose(fp);
I would like not only just read and print the variable but, also, to store them in separate variables. Suggestion on how to do that?
strchr() can help:
while (fgets(str, sizeof str, fp)) {
char *arr[2], *ptr;
arr[0] = str;
if ((ptr = strchr(str, ','))) {
arr[1] = ptr + 1;
*ptr = '\0';
} else {
exit(EXIT_FAILURE);
}
printf("<%s> <%s>\n", arr[0], arr[1]);
}
Notice that you may need to strip the trailing newline lefted by fgets() or ommit the \n in printf
If you need to store those strings in fresh memory, then there is a bit more of work, you can realloc() or use a linked list (always prefer linked lists when you don't know the number of lines before-hand):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node {
void *data;
struct node *next;
};
/* Returns a pointer to an allocated string */
extern char *strdup(const char *);
/* Insert a node and returns a pointer to the data */
static void *enqueue(struct node **root, void *data)
{
struct node *node;
if (root == NULL) {
return NULL;
}
node = malloc(sizeof *node);
if (node == NULL) {
return NULL;
}
if (*root == NULL) {
node->next = node;
} else {
node->next = (*root)->next;
(*root)->next = node;
}
node->data = data;
*root = node;
return data;
}
/* Delete a node and returns a pointer to the data */
static void *dequeue(struct node **root)
{
struct node *node;
void *data = NULL;
if (root == NULL) {
return NULL;
}
node = *root;
if (node != NULL) {
node = node->next;
data = node->data;
if (*root == node) {
*root = NULL;
} else {
(*root)->next = node->next;
}
free(node);
}
return data;
}
int main(void)
{
struct node *head = NULL;
char str[256];
char **arr;
char *ptr;
/* While we don't hit EOF */
while (fgets(str, sizeof str, stdin)) {
/* Reserve space for 2 pointers */
arr = malloc(sizeof(*arr) * 2);
if (arr == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
/* If we find a comma */
if ((ptr = strchr(str, ','))) {
/* Store the second string */
arr[1] = strdup(ptr + 1);
if (arr[1] == NULL) {
perror("strdup");
exit(EXIT_FAILURE);
}
/* Strip the string */
*ptr = '\0';
/* If we don't find a comma */
} else {
exit(EXIT_FAILURE);
}
/* Store the first string */
arr[0] = strdup(str);
if (arr[0] == NULL) {
perror("strdup");
exit(EXIT_FAILURE);
}
/* Add a node to the queue*/
if (enqueue(&head, arr) == NULL) {
perror("enqueue");
exit(EXIT_FAILURE);
}
}
/* While nodes in queue show the data and free */
while ((arr = dequeue(&head))) {
printf("%s %s", arr[0], arr[1]);
free(arr[0]);
free(arr[1]);
free(arr);
}
return 0;
}
You can use fscanf format strings instead.
fscanf(fp, " %[^,\n], %[^\n]", values[i][0], values[i][1]);
Like so
#include <stdio.h>
int main() {
FILE* fp = fopen("file.txt", "r");
char values[1000][2][100];
int i = 0;
while(!feof(fp)) {
fscanf(fp, " %[^,\n], %[^\n]", values[i][0], values[i][1]);
printf("%s %s\n", values[i][0], values[i][1]);
}
}
The fscanf format string:
<space> = skip leading whitespaces (like a newline from the line before)
%[^,\n] = read a string until, but not including, "," or "\n"
, = expect a comma and discard it
%[^\n] = read a string until, but not including, "\n"
values is a 2D array of strings, with 1000 rows and 2 columns. Each element is a string of maximum length of 100 chars.
Also, if you use %m[^,\n] instead, fscanf will allocate the memory for the string read.

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).

Reading from file to dynamic struct

I would like to read from a file, line by line. Each line has 3 arguments guaranteed. First 2 are first and last name and third is age.
I want to make a linked list, in which, each node represents a person (line) in the file.
I don't know the size of the names so I made it dynamic. I also don't know the number of lines in the file, so I would like that to be dynamic too.
My approach was to use fscanf, but then I wouldn't know how much memory needs to be allocated prior to reading it.
The function convertToList is supposed to receive a file path of the file we wanna read, convert it to a linked list, then return the head node. (Open to improvements)
Check out my code and see where I got stuck:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum
{
FALSE,
TRUE
}bool;
struct Node{
char firstName[50];
char lastName[50];
int age;
struct Node *next;
};
typedef struct {
struct Node *head;
}LinkedList;
struct Node * convertToList(char *inputFilePath);
int main(int argc, char* argv[]) {
if(argc != 4) {
printf("Invalid arguments.\n");
exit(0);
}
if (strlen(argv[3])!=1) {
printf("Invalid sorting type.\n");
exit(0);
}
char *inputFilePath = (char*) malloc(sizeof(char*) * strlen(argv[1]) +1);
memcpy(inputFilePath, argv[1], strlen(argv[1]));
char *outputFilePath = (char*) malloc(sizeof(char*) * strlen(argv[2]) +1);
memcpy(outputFilePath, argv[2], strlen(argv[2]) +1);
char *sortType = argv[3];
//LinkedList* inputList = (LinkedList*)malloc(sizeof(struct Node));
struct Node* head = malloc(sizeof(struct Node));
head = convertToList(inputFilePath);
printf("\n%s %s %d\n", head->firstName, head->lastName, head->age);
// printf("\nsaaap\n");
getchar();
}
struct Node * convertToList(char *inputFilePath) {
FILE* ifp;
ifp = fopen(inputFilePath, "r");
if (!ifp) { perror("fopen"); exit(0); }
struct Node *head = NULL;
struct Node *prev = NULL;
bool isHead = TRUE;
while(!feof(ifp)) {
struct Node *tmp = (struct Node*)malloc(sizeof(struct Node));
if (prev != NULL)
prev->next = tmp;
if (head==NULL)
head = tmp;
fscanf(ifp, "%s %s %d\n", tmp->firstName, tmp->lastName, &tmp->age);
prev = tmp;
//Need to link to next node as well
}
fclose(ifp);
return head;
}
I know that the fscanf is wrong, but I'm not sure how to fix it.
Also, how do I return the root? Is my approach gonna work?
And lastly, how do can I set the next node in the list? I don't see it happening with the current while loop.
Thanks.
If you need to link the nodes this is how you can do it and use dynamic storage, here you go, I didn't think this very much but it is Ok.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct Node
{
char *firstName;
char *lastName;
int age;
struct Node *next;
};
struct Node *convertToList(const char *const inputFilePath);
void freeList(struct Node *);
int main(int argc, char* argv[])
{
struct Node *head;
if (argc != 2)
{
printf("Invalid arguments.\n");
return 1;
}
head = convertToList(argv[1]);
if (head != NULL)
{
struct Node *current;
current = head;
while (current != NULL)
{
fprintf(stderr, "%s %s %d\n", current->firstName, current->lastName, current->age);
current = current->next;
}
/* do manupulations with the list, example above, print the values */
freeList(head);
}
return 0;
}
void freeList(struct Node *node)
{
struct Node *current;
current = node;
while (current != NULL)
{
struct Node *next;
next = current->next;
if (current->firstName != NULL)
free(current->firstName);
if (current->lastName != NULL)
free(current->lastName);
free(current);
current = next;
}
}
size_t appendChar(char **buffer, char character, size_t length)
{
char *temporary;
if (buffer == NULL)
return length;
temporary = realloc(*buffer, 1 + length);
if (temporary == NULL)
return length;
temporary[length] = character;
*buffer = temporary;
return 1 + length;
}
struct Node *parseFileLine(char *line)
{
char *word;
struct Node *node;
char *endptr;
if (line == NULL)
return NULL;
node = malloc(sizeof(struct Node));
if (node == NULL)
return NULL;
node->firstName = NULL;
node->lastName = NULL;
node->age = -1; // an invalid value;
node->next = NULL;
word = strtok(line, " ");
if (word == NULL)
return node;
node->firstName = strdup(word);
word = strtok(NULL, " ");
if (word == NULL)
return node;
node->lastName = strdup(word);
word = strtok(NULL, " ");
if (word == NULL)
return node;
node->age = strtol(word, &endptr, 10);
if (*endptr != '\0')
node->age = -1;
return node;
}
struct Node *getNode(FILE *file)
{
char *line;
int character;
size_t length;
line = NULL;
length = 0;
while ((character = fgetc(file)) != EOF)
{
if (((char)character == '\n') && (line != NULL))
{
struct Node *node;
length = appendChar(&line, '\0', length);
node = parseFileLine(line);
free(line);
return node;
}
length = appendChar(&line, (char)character, length);
}
if (line != NULL)
free(line);
return NULL;
}
struct Node *convertToList(const char *const inputFilePath)
{
FILE *ifp;
struct Node *head;
struct Node *current;
struct Node *last;
ifp = fopen(inputFilePath, "r");
if (ifp == NULL)
{
perror("fopen");
return NULL;
}
head = NULL;
last = NULL;
while ((current = getNode(ifp)) != NULL)
{
if (current == NULL)
return head;
if (head == NULL)
head = current;
if (last != NULL)
last->next = current;
last = current;
}
fclose(ifp);
return head;
}
Here you can also print the nodes to see that the data is correctly there.
I think you don't understand what malloc is for and you don't know much about pointers too, in your fscanf you are storing data in firstName and lastName without allocating memory for it, they are not even initialized so you would get a segmentation fault.
A somewhat different approach.
argv copying
First off, as mentioned, you do not need to copy argv values. Main reason for doing do is if you manipulate the values. There are also cases where one want to erase argv values as they can be read by ps and other tools, read from /proc/ etc. For example some programs take passwords as argument, to prevent password to be readable by anyone having access to the system one typically copy the argument then overwrite the argv value.
It is however usually good practice to use variables for the arguments. It usually makes the code clearer, but also makes it easier to maintain if one do changes. E.g. implement flag arguments like -f <filename>.
exit() and return from main()
You also exit() with zero on error. You would want to exit with zero on success, and other value on error or other. This is the norm. 0 == success. Some applications implement numeric exit codes that can mean different things. E.g. 0 is normal exit, 1 is not an error but some special case, 2 likewise 3 might be an error etc. For example grep:
EXIT STATUS
The exit status is 0 if selected lines are found, and 1 if not found. If an
error occurred the exit status is 2. (Note: POSIX error handling code should
check for '2' or greater.)
scanf
When you use scanf to read strings there are some tricks that can be used to make it better. First off always use the size parameter.
char name[16]
sscanf(buf, "%15s", name);
Do also check items read:
if (sscanf(buf, "%15s %d", name, &age) != 2)
... error ...
Third you can also save number of bytes read by %n:
sscanf(buf, "%n%15s%n %n%d%n", &of1, name, &of2, &age, &of3)
Usage
A very simple, but also quick and user-friendly thing, is to add a usage function.
Typically:
int usage(const char *self, const char *err_str)
{
fprintf(stderr,
"Usage: %s <in-file> <out-file> <sort-type>\n"
" Sort types:\n"
" f Sort by First Name\n"
" l Sort by Last Name\n"
" a Sort by Age\n"
,
self
);
if (err_str) {
fprintf(stderr,
"\nError: %s\n",
err_str
);
}
return ERR_ARG;
}
Then in main() you can quickly and clean add something like:
if (argc < 4)
return usage(argv[0], "Missing arguments.");
A note on you validation of the sort argument. Instead of using strlen() you can check if byte 2 is 0.
if (argv[3][1] != '\0')
... error ...
Finally main could be something like:
int main(int argc, char *argv[])
{
char *in_file, *out_file, sort;
struct Node *head = NULL;
int err = 0;
if (argc < 4)
return usage(argv[0], "Missing arguments.");
if (argc > 4)
return usage(argv[0], "Unknown arguments.");
if (argv[3][1] != '\0')
return usage(argv[0], "Invalid sorting type.");
in_file = argv[1];
out_file = argv[2];
sort = argv[3][0];
if (sort != 'f' && sort != 'l' && sort != 'a')
return usage(argv[0], "Invalid sorting type.");
if ((err = file_to_llist(in_file, &head)) != 0)
return err;
prnt_llist(stdout, head);
free_ll(head);
return err;
}
malloc helpers
When dealing with a lot of mallocing and similar it can be useful to add some helper functions. If you get a memory error you normally would exit right away.
void *alloc(size_t size)
{
void *buf;
if ((buf = malloc(size)) == NULL) {
fprintf(stderr, "Memory error.\n");
exit(ERR_MEM);
}
return buf;
}
void *re_alloc(void *old, size_t size)
{
void *buf;
if ((buf = realloc(old, size)) == NULL) {
fprintf(stderr, "Memory error.\n");
exit(ERR_MEM);
}
return buf;
}
Parsing of the file
As you want to have everything dynamically allocated and no limits (beyond system memory) one solution is to implement some sort of tokenizer. It can be helpful to use a struct to hold it together. Something like:
struct file_toker {
FILE *fh; /* File handle */
char *buf; /* Dynamic Read buffer */
size_t size; /* Size of buffer */
size_t len; /* Length of actual data in buffer. */
};
One point here is to keep length of tokens read. By this one do not need to keep using strlen etc.
If you can afford it it would usually be better to read whole file in one go, then parse the buffer. Optionally one can read file in chunks of say 4096*16 bytes, but then one get some complexity when it comes to overlapping lines between reads etc.
Anyhow in this example one byte is read at a time.
Start code
Finally a starting ground could be something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* memcpy/strncpy */
#include <errno.h> /* errno for fopen() */
#include <ctype.h> /* isspace() */
#define ERR_ARG 1
#define ERR_FILE_FMT 2
#define ERR_MEM 3
struct Node {
char *name_first;
char *name_last;
int age;
struct Node *next;
};
struct file_toker {
FILE *fh;
char *buf;
size_t size;
size_t len;
};
/* ===============----- GEN HELPERS ------=================== */
int usage(const char *self, const char *err_str)
{
fprintf(stderr,
"Usage: %s <in-file> <out-file> <sort-type>\n"
" Sort types:\n"
" f Sort by First Name\n"
" l Sort by Last Name\n"
" a Sort by Age\n"
,
self
);
if (err_str) {
fprintf(stderr,
"\nError: %s\n",
err_str
);
}
return ERR_ARG;
}
void *alloc(size_t size)
{
void *buf;
if ((buf = malloc(size)) == NULL) {
fprintf(stderr, "Memory error.\n");
exit(ERR_MEM);
}
return buf;
}
void *re_alloc(void *old, size_t size)
{
void *buf;
if ((buf = realloc(old, size)) == NULL) {
fprintf(stderr, "Memory error.\n");
exit(ERR_MEM);
}
return buf;
}
/* ===============----- LINKED LIST ------=================== */
void free_node(struct Node *n)
{
if (!n)
return;
if (n->name_first)
free(n->name_first);
if (n->name_last)
free(n->name_last);
free(n);
}
void free_ll(struct Node *n)
{
struct Node *p;
if (!n)
return;
for ( ; n ; ) {
p = n;
n = n->next;
free_node(p);
}
}
void prnt_llist(FILE *fd, struct Node *n)
{
int i = 0;
fprintf(fd, "NODELIST:\n");
for ( ; n != NULL ; n = n->next) {
fprintf(fd,
"Entry %d {\n"
" Name: %s, %s\n"
" Age : %d\n"
"}\n",
++i,
n->name_last,
n->name_first,
n->age
);
}
}
/* ================--------- FILE TOKER ------------==================== */
/* Free / close reader. */
void free_ft(struct file_toker *ft)
{
if (!ft)
return;
if (ft->fh)
fclose(ft->fh);
free(ft->buf);
ft->fh = NULL;
ft->buf = NULL;
}
/* Initiate reader. */
int ft_init(struct file_toker *ft, const char *fn, size_t buf_sz)
{
ft->size = buf_sz;
ft->len = 0;
ft->buf = alloc(ft->size);
ft->fh = fopen(fn, "r");
if (!ft->fh) {
perror("Unable to open file");
return errno;
}
return 0;
}
/* Increase buffer size. */
size_t ft_increase(struct file_toker *ft)
{
if (ft->size < 1)
ft->size = 1;
ft->size *= 2;
ft->buf = re_alloc(ft->buf, ft->size);
return ft->size;
}
/* Read and skip spaces (\n, \r, ' ', \t etc.). Return first non-space. */
char ft_skip_space(struct file_toker *ft)
{
int c;
while ((c = fgetc(ft->fh)) != EOF && isspace(c))
;
return c == EOF ? 0 : (char)c;
}
/* Read next token */
size_t file_tok(struct file_toker *ft)
{
size_t i = 1;
size_t max;
int c;
if (ft->size < 2)
ft_increase(ft);
ft->len = 0;
max = ft->size - 1;
/* Skip any leading spaces. Function return first non-space. */
if ((ft->buf[0] = ft_skip_space(ft)) == 0)
return 0;
while ((c = fgetc(ft->fh)) != EOF) {
/* If space, break. */
if (isspace(c))
break;
/* Save char to buffer. */
ft->buf[i++] = (char)c;
/* If entire buffer used, increase it's size. */
if (i > max)
max = ft_increase(ft) - 1;
}
/* Null terminate. */
ft->buf[i] = 0x00;
/* Length without terminating null */
ft->len = i;
return i;
}
/* Read next space separated token and save it as new allocated string. */
int file_tok_str(struct file_toker *ft, char **out)
{
if (file_tok(ft) == 0)
return 1;
*out = alloc(ft->len + 1);
memcpy(*out, ft->buf, ft->len + 1);
return 0;
}
/* Read next space separated token and scan it as int. */
int file_tok_int(struct file_toker *ft, int *out)
{
if (file_tok(ft) == 0)
return 1;
if ((sscanf(ft->buf, "%d", out)) != 1)
return 1;
return 0;
}
/* ===============----- FILE PARSER ------=================== */
int file_to_llist(const char *fn, struct Node **head)
{
struct Node *node = NULL, *cur = *head;
struct file_toker ft;
/* Initiate new file token reader, initial buffer size 4096 bytes. */
if (ft_init(&ft, fn, 4096))
return 1;
while (1) {
/* Allocate next node */
node = alloc(sizeof(struct Node));
node->name_first = NULL;
node->name_last = NULL;
/* Read and copy first name. */
if (file_tok_str(&ft, &node->name_first))
break;
/* Read and copy last name. */
if (file_tok_str(&ft, &node->name_last))
break;
/* Read and copy age. */
if (file_tok_int(&ft, &node->age))
break;
/* Link and save current for next iteration. */
node->next = NULL;
if (cur) {
cur->next = node;
}
cur = node;
if (*head == NULL)
*head = node;
}
/* Free last unused node. */
free_node(node);
free_ft(&ft);
return 0;
}
/* ===============----- MAIN ROUTINE ------=================== */
int main(int argc, char *argv[])
{
char *in_file, *out_file, sort;
struct Node *head = NULL;
int err = 0;
if (argc < 4)
return usage(argv[0], "Missing arguments.");
if (argc > 4)
return usage(argv[0], "Unknown arguments.");
if (argv[3][1] != '\0')
return usage(argv[0], "Invalid sorting type.");
in_file = argv[1];
out_file = argv[2];
sort = argv[3][0];
if (sort != 'f' && sort != 'l' && sort != 'a')
return usage(argv[0], "Invalid sorting type.");
if ((err = file_to_llist(in_file, &head)) != 0)
return err;
prnt_llist(stdout, head);
free_ll(head);
return err;
}

Resources