Reputation: 119
I am trying to insert the contents from a csv file into a linklist using C. However, I am getting quite a few garbage outputs. The source code is given below.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct product *customer_head;
struct customer
{
long long int c_id;//first 6 characters=date & next 6 characters=time & next characters=counter no & city code
/*Compulsory Fields (Cannot be Skipped)*/
char name[57];
long long int ph_no;//10 digit phone number
/*Non Compulsory Fields (Can be Skipped)*/
char address[58];
char city[25];
char state_code[2];
char pin[6];
char email[60];
struct customer *next;
};
struct customer * load()
{
FILE * cust=fopen("customer_db.csv","r");
struct customer *temp,*ptr;
customer_head=NULL;
char str[208];
char *token,*eptr1,*eptr2;
int line_cnt=0,i=0;
while(fgets(str,234,cust)!=NULL)
{
line_cnt=0;
i=0;
ptr=(struct customer *)malloc(sizeof(struct customer));
for(;str[i];i++)
{
if(str[i]=='\n')
{
str[i]='\0';
i=0;
break;
}
}
token=strtok(str,",");
while(token!=NULL)
{
if(line_cnt==0)
ptr->c_id=strtoll(token,&eptr1,10);
else if(line_cnt==1)
ptr->ph_no=strtoll(token,&eptr2,10);
else if(line_cnt==2)
sprintf(ptr->name,"%s",token);
else if(line_cnt==3)
sprintf(ptr->address,"%s",token);
else if(line_cnt==4)
sprintf(ptr->city,"%s",token);
else if(line_cnt==5)
sprintf(ptr->state_code,"%s",token);
else if(line_cnt==6)
sprintf(ptr->pin,"%s",token);
else
sprintf(ptr->email,"%s",token);
line_cnt++;
token=strtok(NULL,",");
}
if(customer_head==NULL)
customer_head=ptr;
else
temp->next=ptr;
temp=ptr;
}
}
int print(struct customer *h)
{
while(h->next!=NULL)
{
printf("\nCustomer ID: ");
printf("%lld",h->c_id);
printf("\nName: ");
puts(h->name);
printf("Phone Number: ");
printf("%lld",h->ph_no);
printf("\nAddress: ");
puts(h->address);
printf("City: ");
puts(h->city);
printf("State Code: ");
puts(h->state_code);
printf("PIN: ");
puts(h->pin);
printf("Email: ");
puts(h->email);
h=h->next;
}
printf("\nCustomer ID: ");
printf("%lld",h->c_id);
printf("\nName: ");
puts(h->name);
printf("Phone Number: ");
printf("%ld",h->ph_no);
printf("\nAddress: ");
puts(h->address);
printf("City: ");
puts(h->city);
printf("State Code: ");
puts(h->state_code);
printf("PIN: ");
puts(h->pin);
printf("Email: ");
puts(h->email);
return 1;
}
int main()
{
load();
print(customer_head);
}
I am also attaching the csv file here. In order for the program to be less complicated I have removed the headings from my csv file. They are in the order
Customer_ID,Phone_Number,Name,Address,City,State_Code,PIN,Email
1403201156540201,2226179183,Katherine_Hamilton,87_Thompson_St.,Fremont,IA,502645,[email protected]
2204201532220103,8023631298,Marc_Knight,-,-,-,-,-
0305201423120305,8025595163,Albie_Rowland,-,Hamburg,NY,140752,-
0607201232220901,4055218053,Grant_Phelps,-,-,-,-,-
The dashes(-) indicate that those fields should remain empty.
The output is as follows:
Customer ID: 1403201156540201
Name: Katherine_Hamilton
Phone Number: 2226179183
Address: 87_Thompson_St.
City: Fremont
State Code: [email protected]
PIN: [email protected]
Email: [email protected]
Customer ID: 2204201532220103
Name: Marc_Knight
Phone Number: 8023631298
Address: -
City: -
State Code: -
PIN: -
Email: -
Customer ID: 305201423120305
Name: Albie_Rowland
Phone Number: 8025595163
Address: -
City: Hamburg
State Code: NY140752-
PIN: 140752-
Email: -
Customer ID: 607201232220901
Name: Grant_Phelps
Phone Number: 4055218053
Address: -
City: -
State Code: -
PIN: -
Email: -
As you can see, the contents are getting merged in quite a few places. I don't understand why.
Upvotes: 1
Views: 2166
Reputation: 967
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum operations {EXIT, ADD_BOOK, ISSUE_BOOK, RETURN_BOOK, DISPLAY_BOOKS,
SORT_BOOKS, DISPLAY_BOOK_SUBJECTWISE};
typedef enum operations MENU;
typedef struct book
{
int bookId;
char bookName[30];
char subject[30];
char author[30];
float price;
}BOOK;
typedef struct node
{
struct node *prev;
BOOK info;
struct node *next;
}NODE;
NODE *head=NULL;
NODE *issuehead=NULL;
static int counter=0;
MENU menu_function()
{
int choice;
printf(" 1. Add Book\n");
printf(" 2. Issue Book\n");
printf(" 3. Return Book\n");
printf(" 4. Display Book\n");
printf(" 5. Sort Book\n");
printf(" 6. Display Books Subject-wise\n");
printf(" 0. EXIT\n");
printf(" Enter your choice::");
scanf("%d",&choice);
if(choice < 0 || choice > 6)
{
printf(" Error: Enter valid choice\n");
}
return choice;
}
void FreeList()
{
while(CountNodes(head))
{
DeleteBook();
}
while(CountNodes(issuehead))
{
DeleteIssues();
}
}
int CountNodes(NODE *trav)
{
int count=0;
while(trav!=NULL)
{
count++;
trav=trav->next;
}
return count;
}
void DeleteBook()
{
NODE *temp=head;
head = head->next;
free(temp);
temp=NULL;
}
void DeleteIssues()
{
NODE *temp=issuehead;
issuehead = issuehead->next;
free(temp);
temp=NULL;
}
void AcceptData(BOOK *book)
{
book->bookId=++counter;
getchar();
printf(" Enter Book name::");
scanf("%[^\n]s",&book->bookName);
getchar();
printf(" Enter Subject::");
scanf("%[^\n]s",&book->subject);
getchar();
printf(" Enter author::");
scanf("%[^\n]s",&book->author);
printf(" Enter price::");
scanf("%f",&book->price);
}
void DisplayData(BOOK book)
{
printf(" %d\t\t",book.bookId);
printf(" %s\t\t",book.bookName);
printf(" %s\t\t",book.subject);
printf(" %s\t\t",book.author);
printf(" %g\n",book.price);
}
NODE *CreateNode()
{
NODE *temp;
temp = (NODE *) malloc(sizeof(NODE));
temp->next=NULL;
temp->prev=NULL;
return temp;
}
void AddtoBooklist(BOOK book)
{
NODE *new_node;
new_node=CreateNode();
new_node->info=book;
if(head == NULL)
{
head=new_node;
}
else
{
new_node->next=head;
head->prev=new_node;
head=new_node;
}
}
void DisplayBooks(NODE *trav)
{
if(trav==NULL)
{
printf(" Book list is empty...\n");
}
else
{
printf(" Available Books\n");
while(trav!=NULL)
{
DisplayData(trav->info);
trav=trav->next;
}
printf("\n");
}
}
void IssueBook()
{
NODE *trav=head, *prev=NULL;
NODE *temp, *right;
int bookId;
printf(" Enter Book ID::");
scanf("%d",&bookId);
while(bookId != trav->info.bookId)
{
prev=trav;
trav=trav->next;
if(trav==NULL)
{
printf(" Book not found...\n");
break;
}
}
if(trav==head)
{
temp = trav;
head->prev=NULL;
head = head->next;
trav=NULL;
IssueAtFirst(temp);
printf(" Book issued successfully...\n");
}
else if(trav->next==NULL)
{
temp=trav;
prev->next=NULL;
trav->prev=NULL;
trav=NULL;
IssueAtFirst(temp);
printf(" Book issued successfully...\n");
}
else
{
temp=trav;
right=trav->next;
prev->next=right;
right->prev=prev;
trav->next=NULL;
trav->prev=NULL;
trav=NULL;
IssueAtFirst(temp);
printf(" Book issued successfully...\n");
}
}
void IssueAtFirst(NODE *temp)
{
if(issuehead == NULL)
{
issuehead=temp;
temp->next=NULL;
temp->prev=NULL;
temp=NULL;
}
else
{
temp->next=issuehead;
temp->prev=NULL;
issuehead=temp;
temp=NULL;
}
}
void ReturnBook()
{
NODE *trav=issuehead, *prev=NULL;
NODE *temp, *right;
int bookId;
printf(" Enter Book ID::");
scanf("%d",&bookId);
while(bookId != trav->info.bookId)
{
prev=trav;
trav=trav->next;
if(trav==NULL)
{
printf(" Book not found...\n");
break;
}
}
if(trav==issuehead)
{
temp = trav;
issuehead->prev = NULL;
issuehead = issuehead->next;
trav=NULL;
ReturnAtFirst(temp);
printf(" Book returned successfully...\n");
}
else if(trav->next==NULL)
{
temp=trav;
prev->next=NULL;
trav->prev=NULL;
trav=NULL;
ReturnAtFirst(temp);
printf(" Book returned successfully...\n");
}
else
{
temp=trav;
right=trav->next;
prev->next=right;
right->prev=prev;
trav->next=NULL;
trav->prev=NULL;
trav=NULL;
ReturnAtFirst(temp);
printf(" Book returned successfully...\n");
}
}
void ReturnAtFirst(NODE *temp)
{
if(head == NULL)
{
head=temp;
temp->next=NULL;
temp->prev=NULL;
temp=NULL;
}
else
{
head->prev=temp;
temp->next=head;
temp->prev=NULL;
head=temp;
temp=NULL;
}
}
void SortBooks()
{
NODE *trav=head,*right=head->next;
BOOK temp;
while(trav->next!=NULL)
{
right=trav->next;
while(right!=NULL)
{
if(trav->info.bookId > right->info.bookId)
{
temp = trav->info;
trav->info = right->info;
right->info = temp;
}
right=right->next;
}
trav=trav->next;
}
}
void AddBooksToFile()
{
NODE *trav=head;
FILE *fp;
fp=fopen("Booklist.dat","wb");
if(fp!=NULL)
{
while(trav!=NULL)
{
fwrite(&trav->info, sizeof(BOOK),1,fp);
trav=trav->next;
}
}
fclose(fp);
}
void AddIssuesToFile()
{
NODE *trav=issuehead;
FILE *fp=issuehead;
fp=fopen("Issuelist.dat","wb");
if(fp!=NULL)
{
while(trav!=NULL)
{
fwrite(&trav->info, sizeof(BOOK),1,fp);
trav=trav->next;
}
}
fclose(fp);
}
void ReadIssuesFromFile()
{
BOOK book;
FILE *fp;
fp=fopen("Issuelist.dat","rb");
if(fp!=NULL)
{
while((fread(&book,sizeof(BOOK),1,fp))!=0)
{
AddtoIssuelist(book);
}
}
fclose(fp);
}
void ReadBooksFromFile()
{
BOOK book;
FILE *fp;
fp=fopen("Booklist.dat","rb");
if(fp!=NULL)
{
while((fread(&book,sizeof(BOOK),1,fp))!=0)
{
AddtoBooklist(book);
}
}
fclose(fp);
}
void AddtoIssuelist(BOOK book)
{
NODE *new_node;
new_node=CreateNode();
new_node->info=book;
if(issuehead == NULL)
{
issuehead=new_node;
}
else
{
new_node->next=issuehead;
issuehead->prev=new_node;
issuehead=new_node;
}
}
void DisplaySubjectWise(NODE *head)
{
NODE *trav=head;
char subject[30];
printf(" Enter subject::");
scanf("%s",&subject);
while(trav!=NULL)
{
if(stricmp(subject, trav->info.subject)== 0)
{
DisplayData(trav->info);
}
trav=trav->next;
}
}
int main()
{
MENU choice;
int data;
BOOK book;
ReadBooksFromFile();
ReadIssuesFromFile();
while((choice=menu_function())!=EXIT)
{
switch(choice)
{
case ADD_BOOK:
AcceptData(&book);
AddtoBooklist(book);
printf(" Book added to library successfully...\n");
break;
case ISSUE_BOOK:
if(head==NULL)
{
printf(" No Books are available now...\n");
break;
}
else
{
IssueBook();
}
break;
case RETURN_BOOK:
if(issuehead==NULL)
{
printf(" No Books are issued...\n");
break;
}
else
{
ReturnBook();
}
break;
case DISPLAY_BOOKS:
DisplayBooks(head);
DisplayBooks(issuehead);
printf("\n");
break;
case SORT_BOOKS:
if(head==NULL)
{
printf(" No Books to sort...\n");
break;
}
SortBooks();
break;
case DISPLAY_BOOK_SUBJECTWISE:
if(head==NULL)
{
printf(" No Books to display...\n");
break;
}
DisplaySubjectWise(head);
break;
}
}
AddBooksToFile();
AddIssuesToFile();
FreeList();
return 0;
}
for your reference
Upvotes: 0
Reputation: 84569
Since from the comments you know that your declaration of your character arrays suffer from one-too-few character, at least in the case of char state_code[2];
leaving your array without a nul-terminating character leading to Undefined Behavior, you should ensure you have valid storage for all your input. (don't skimp on buffer size)
In general, you are making things a bit harder on yourself than it needs to be. Rather than attempting to use strtok()
and count field and handle each field in a 8-part chain of if, else if ...
, you have fixed input fields, so just parse the data with sscanf()
and validate the number of conversions to confirm a successful parse, e.g.
/** fill list from csv file */
list_t *list_from_csv (list_t *list, FILE *fp)
{
char buf[MAXC];
node_t data = { .c_id = 0, .next = NULL };
while (fgets (buf, MAXC, fp)) { /* read each line in file */
/* parse and VALIDATE values from line */
if (sscanf (buf, "%lld,%lld,%63[^,],%63[^,],%31[^,],%7[^,],%7[^,],%63[^,\n]",
&data.c_id, &data.ph_no, data.name, data.address, data.city,
data.state_code, data.pin, data.email) == 8) {
if (!add (list, &data)) /* validate add to list or break */
break;
}
}
return list;
}
Here, list_t
is just an additional "wrapper" struct that holds a head
and tail
pointer for your linked list. This allows you to declare multiple lists in the scope needed and allows the same O(1) insertion by having the tail
pointer always point to the last node in your list (your temp
). Here, head
and tail
are just part of the wrapper and passed as a parameter rather than having to declare the list-pointer as global (bad practice). Each of the nodes in your list and the wrapper struct can be written as:
#define BYTE8 8 /* if you need a constant, #define one (or more) */
#define BYTE32 32
#define BYTE64 64
#define MAXC 1024
typedef struct node_t { /* list node */
/* 6 characters=date & 6 characters=time & counter no & city code */
long long int c_id;
/*Compulsory Fields (Cannot be Skipped)*/
char name[BYTE64];
long long int ph_no; //10 digit phone number
/*Non Compulsory Fields (Can be Skipped)*/
char address[BYTE64];
char city[BYTE32];
char state_code[BYTE8];
char pin[BYTE8];
char email[BYTE64];
struct node_t *next;
} node_t;
typedef struct { /* list wrapper with head & tail pointers */
node_t *head, *tail;
} list_t;
Then rather than writing your load()
to contain both the FILE
and list operations, keep your list operations separate. Simply create an add()
function to add a node to your list, e.g.
/** add node at end of list, update tail to end */
node_t *add (list_t *l, node_t *data)
{
node_t *node = malloc (sizeof *node); /* allocate node */
if (!node) { /* validate allocation */
perror ("malloc-node");
return NULL;
}
*node = *data; /* initialize members values */
if (!l->head) /* if 1st node, node is head/tail */
l->head = l->tail = node;
else { /* otherwise */
l->tail->next = node; /* add at end, update tail pointer */
l->tail = node;
}
return node; /* return new node */
}
Now your load function need only read each line from the file and parse the line before calling add()
passing a pointer to a struct of data along with the list pointer as parameters. Your load()
function reduces to:
/** fill list from csv file */
list_t *list_from_csv (list_t *list, FILE *fp)
{
char buf[MAXC];
node_t data = { .c_id = 0, .next = NULL };
while (fgets (buf, MAXC, fp)) { /* read each line in file */
/* parse and VALIDATE values from line */
if (sscanf (buf, "%lld,%lld,%63[^,],%63[^,],%31[^,],%7[^,],%7[^,],%63[^,\n]",
&data.c_id, &data.ph_no, data.name, data.address, data.city,
data.state_code, data.pin, data.email) == 8) {
if (!add (list, &data)) /* validate add to list or break */
break;
}
}
return list;
}
(note: when using strtok()
or sscanf()
, there is no need to strip the trailing '\n'
from your input string -- simply include that as a delimiter for strtok()
or exclude it from the conversion with sscanf()
)
Further, you don't need multiple calls to puts()
and printf()
to print each node's worth of data in your list. Look at how many function calls you make to print your data. You only need ONE call to printf()
, e.g.
/** print all nodes in list */
void prn_list (list_t *l)
{
if (!l->head) {
puts ("list-empty");
return;
}
for (node_t *n = l->head; n; n = n->next)
printf ("\nCustomer ID: %lld\n"
"Name: %s\n"
"Phone Number: %lld\n"
"Address: %s\n"
"City: %s\n"
"State Code: %s\n"
"PIN: %s\n"
"Email: %s\n", n->c_id, n->name, n->ph_no, n->address, n->city,
n->state_code, n->pin, n->email);
}
In main()
simply declare an instance of your list_t
wrapper, open/validate your FILE
and then pass a pointer to the list and file-stream to your list_from_csv()
(your load()
) and then print the list and finally free all memory you have allocated and you are done. (yes, the memory will be freed on exit, but develop good habits early -- it won't be long before you are using allocate memory in function where a failure to free before return
results in a memory-leak)
int main (int argc, char **argv) {
list_t list = { .head = NULL, .tail = NULL };
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (!list_from_csv (&list, fp))
return 1;
if (fp != stdin) /* close file if not stdin */
fclose (fp);
prn_list (&list);
del_list (&list);
}
Putting it altogether, you would have something similar to the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BYTE8 8 /* if you need a constant, #define one (or more) */
#define BYTE32 32
#define BYTE64 64
#define MAXC 1024
typedef struct node_t { /* list node */
/* 6 characters=date & 6 characters=time & counter no & city code */
long long int c_id;
/*Compulsory Fields (Cannot be Skipped)*/
char name[BYTE64];
long long int ph_no; //10 digit phone number
/*Non Compulsory Fields (Can be Skipped)*/
char address[BYTE64];
char city[BYTE32];
char state_code[BYTE8];
char pin[BYTE8];
char email[BYTE64];
struct node_t *next;
} node_t;
typedef struct { /* list wrapper with head & tail pointers */
node_t *head, *tail;
} list_t;
/** add node at end of list, update tail to end */
node_t *add (list_t *l, node_t *data)
{
node_t *node = malloc (sizeof *node); /* allocate node */
if (!node) { /* validate allocation */
perror ("malloc-node");
return NULL;
}
*node = *data; /* initialize members values */
if (!l->head) /* if 1st node, node is head/tail */
l->head = l->tail = node;
else { /* otherwise */
l->tail->next = node; /* add at end, update tail pointer */
l->tail = node;
}
return node; /* return new node */
}
/** print all nodes in list */
void prn_list (list_t *l)
{
if (!l->head) {
puts ("list-empty");
return;
}
for (node_t *n = l->head; n; n = n->next)
printf ("\nCustomer ID: %lld\n"
"Name: %s\n"
"Phone Number: %lld\n"
"Address: %s\n"
"City: %s\n"
"State Code: %s\n"
"PIN: %s\n"
"Email: %s\n", n->c_id, n->name, n->ph_no, n->address, n->city,
n->state_code, n->pin, n->email);
}
/** delete all nodes in list */
void del_list (list_t *l)
{
node_t *n = l->head;
while (n) {
node_t *victim = n;
n = n->next;
free (victim);
}
}
/** fill list from csv file */
list_t *list_from_csv (list_t *list, FILE *fp)
{
char buf[MAXC];
node_t data = { .c_id = 0, .next = NULL };
while (fgets (buf, MAXC, fp)) { /* read each line in file */
/* parse and VALIDATE values from line */
if (sscanf (buf, "%lld,%lld,%63[^,],%63[^,],%31[^,],%7[^,],%7[^,],%63[^,\n]",
&data.c_id, &data.ph_no, data.name, data.address, data.city,
data.state_code, data.pin, data.email) == 8) {
if (!add (list, &data)) /* validate add to list or break */
break;
}
}
return list;
}
int main (int argc, char **argv) {
list_t list = { .head = NULL, .tail = NULL };
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (!list_from_csv (&list, fp))
return 1;
if (fp != stdin) /* close file if not stdin */
fclose (fp);
prn_list (&list);
del_list (&list);
}
Example Use/Output
With your input file in dat/customer_list.txt
, running the program you would receive:
$ ./bin/customer_list dat/customer_list.txt
Customer ID: 1403201156540201
Name: Katherine_Hamilton
Phone Number: 2226179183
Address: 87_Thompson_St.
City: Fremont
State Code: IA
PIN: 502645
Email: [email protected]
Customer ID: 2204201532220103
Name: Marc_Knight
Phone Number: 8023631298
Address: -
City: -
State Code: -
PIN: -
Email: -
Customer ID: 305201423120305
Name: Albie_Rowland
Phone Number: 8025595163
Address: -
City: Hamburg
State Code: NY
PIN: 140752
Email: -
Customer ID: 607201232220901
Name: Grant_Phelps
Phone Number: 4055218053
Address: -
City: -
State Code: -
PIN: -
Email: -
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to ensure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/customer_list dat/customer_list.txt
==14823== Memcheck, a memory error detector
==14823== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==14823== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==14823== Command: ./bin/customer_list dat/customer_list.txt
==14823==
Customer ID: 1403201156540201
Name: Katherine_Hamilton
Phone Number: 2226179183
Address: 87_Thompson_St.
City: Fremont
State Code: IA
PIN: 502645
Email: [email protected]
<snipped rest>
==14823==
==14823== HEAP SUMMARY:
==14823== in use at exit: 0 bytes in 0 blocks
==14823== total heap usage: 7 allocs, 7 frees, 6,728 bytes allocated
==14823==
==14823== All heap blocks were freed -- no leaks are possible
==14823==
==14823== For counts of detected and suppressed errors, rerun with: -v
==14823== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
While you could cure a significant part of your problem just by ensuring you have adequate storage for each string, take the time to think through approaching the parse of values with sscanf()
and since you control the conversion, there is no need to remove the trailing '\n'
from the line read from the file. (just don't include the newline in the value parsed from your input string) If you did want to parse the '\n'
from the end, you should be using, e.g.
str[strcspn (str, "\n")] = 0;
Lastly with both the format-string used with sscanf()
and with strcspn()
above, make sure you understand exactly how they work, see man 3 scanf and man 3 strspn
Let me know if you have further questions.
Upvotes: 1