Как сохранить содержимое файла CSV в связанном списке? - PullRequest
1 голос
/ 28 мая 2020

Я пытаюсь вставить содержимое из файла csv в список ссылок, используя C. Однако я получаю довольно много мусора. Исходный код приведен ниже.

#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);
}

Я также прикрепляю сюда файл csv. Чтобы программа была менее сложной, я удалил заголовки из моего файла csv. Они расположены в следующем порядке:

Customer_ID, Phone_Number, Name, Address, City, State_Code, PIN, Email

1403201156540201,2226179183,Katherine_Hamilton,87_Thompson_St.,Fremont,IA,502645,k_hamilton@gmail.com
2204201532220103,8023631298,Marc_Knight,-,-,-,-,-
0305201423120305,8025595163,Albie_Rowland,-,Hamburg,NY,140752,-
0607201232220901,4055218053,Grant_Phelps,-,-,-,-,-

Прочерки (-) означают, что эти поля должны оставаться пустыми.

Результат выглядит следующим образом:

Customer ID: 1403201156540201
Name: Katherine_Hamilton
Phone Number: 2226179183
Address: 87_Thompson_St.
City: Fremont
State Code: IA502645k_hamilton@gmail.com
PIN: 502645k_hamilton@gmail.com
Email: k_hamilton@gmail.com

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: -

Как видите, содержимое объединяется в нескольких местах. Не понимаю почему.

Ответы [ 2 ]

1 голос
/ 28 мая 2020

Поскольку из комментариев вы знаете, что ваше объявление ваших символьных массивов страдает от символа one-too-small , по крайней мере, в случае char state_code[2];, оставляющего ваш массив без nul- завершающий символ , ведущий к Undefined Behavior , вы должны убедиться, что у вас есть допустимое хранилище для всего вашего ввода. (не экономьте на размере буфера)

В общем, вы усложняете себе жизнь немного сложнее, чем это должно быть. Вместо того, чтобы пытаться использовать strtok() и поле подсчета и обрабатывать каждое поле в цепочке из 8 частей if, else if ..., у вас есть фиксированные поля ввода, поэтому просто проанализируйте данные с помощью sscanf() и проверьте количество преобразований, чтобы подтвердить успешный синтаксический анализ, например,

/** 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;
}

Здесь list_t - это просто дополнительная структура-оболочка, которая содержит указатели head и tail для вашего связанного списка. Это позволяет вам объявлять несколько списков в необходимой области и позволяет вставлять одну и ту же O (1), поскольку указатель tail всегда указывает на последний узел в вашем списке (ваш temp). Здесь head и tail являются просто частью оболочки и передаются как параметр, вместо того, чтобы объявлять указатель на список как глобальный (плохая практика). Каждый из узлов в вашем списке и структура-оболочка могут быть записаны как:

#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;

Тогда вместо того, чтобы писать load(), содержащий как FILE, так и операции со списком, храните операции со списком отдельно. Просто создайте функцию add(), чтобы добавить узел в ваш список, например,

/** 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 */
}

Теперь вашей функции загрузки нужно только прочитать каждую строку из файла и проанализировать строку перед вызовом add(), передав указателя на структура данных вместе с указателем списка в качестве параметров. Ваша функция load() сокращается до:

/** 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;
}

( примечание: при использовании strtok() или sscanf(), нет необходимости удалять конечный '\n' из вашего ввода строка - просто включите это как разделитель для strtok() или исключите его из преобразования с помощью sscanf())

Кроме того, вам не нужно несколько вызовов puts() и printf() для печати каждого значение узла данных в вашем списке. Посмотрите, сколько вызовов функций вы делаете для печати данных. Вам нужен только ОДИН вызов printf(), например

/** 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);
}

В main() просто объявите экземпляр вашей list_t оболочки, откройте / подтвердите свой FILE, а затем передайте указатель на список и поток файлов на ваш list_from_csv() (ваш load()), а затем распечатайте список и, наконец, освободите всю выделенную вами память, и все готово. (да, память будет освобождена при выходе, но выработайте полезные привычки на ранней стадии - совсем скоро вы начнете использовать функцию выделения памяти в функции, когда неспособность освободить до return приводит к утечке памяти )

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);
}

В совокупности у вас будет что-то похожее на следующее:

#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);
}

Пример использования / вывода

С вашим входным файлом в dat/customer_list.txt, запустив программу, вы получите:

$ ./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: k_hamilton@gmail.com

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: -

Использование памяти / Проверка ошибок

В любом коде, который вы пишете динамически выделяет память, у вас есть 2 обязанности в отношении любого выделенного блока памяти: (1) всегда сохранять указатель на начальный адрес для блока памяти, поэтому (2) это может быть освобожден , когда он больше не нужен.

Крайне важно, чтобы вы использовали программу проверки ошибок памяти, чтобы убедиться, что вы не пытаетесь получить доступ к памяти или писать за пределами / за пределами выделенного вами заблокировать, попытаться прочитать или b условный переход к неинициализированному значению и, наконец, подтверждение того, что вы освободили всю выделенную память.

Для Linux valgrind - это нормальный выбор. Подобные средства проверки памяти существуют для каждой платформы. Все они просты в использовании, просто запустите свою программу через нее.

$ 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: k_hamilton@gmail.com

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

Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.

Хотя вы можете вылечить значительную часть своей проблемы, просто обеспечив достаточное хранилище для каждой строки, найдите время, чтобы подумать о приближении к синтаксическому анализу значений с помощью sscanf(), и, поскольку вы контролируете преобразование, в этом нет необходимости чтобы удалить завершающий '\n' из строки, прочитанной из файла. (просто не включайте новую строку в значение, анализируемое из вашей входной строки) Если вы действительно хотите проанализировать '\n' с конца, вы должны использовать, например,

    str[strcspn (str, "\n")] = 0;

Наконец, с обоими строка формата, используемая с sscanf() и с strcspn() выше, убедитесь, что вы точно понимаете, как они работают, см. man 3 scanf и man 3 strspn

Дайте мне знать, если у вас возникнут дополнительные вопросы.

0 голосов
/ 28 мая 2020
#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;
}

для справки

...