Thread: Write to files a linked list of structures

  1. #1
    Registered User
    Join Date
    Sep 2011
    Posts
    78

    Write to files a linked list of structures

    My problem is as follows: write to files a linked list of structures to save a database and then later you can open that file, read the structures, recreating the linked list and work on it.
    In what ways I open the file? What is the function used to write data?
    If you can post some sample code, thanks.

  2. #2
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Have a look here:

    SourceForge.net: Serialization - cpwiki

    Linked lists are referred to a few times. Beware the "Caveat" (about pointers) at the bottom!
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  3. #3
    Registered User
    Join Date
    Sep 2011
    Posts
    78
    If I wanted to write directly any structure to a file without putting all the nodes in a vector?

  4. #4
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by drew99 View Post
    My problem is as follows: write to files a linked list of structures to save a database and then later you can open that file, read the structures, recreating the linked list and work on it.
    In what ways I open the file? What is the function used to write data?
    If you can post some sample code, thanks.
    Is this homework? If so then disregard what's below and follow the assignment....

    If it's not homework, ask yourself : Do you really need all that data in memory at the same time?

    Most data processing software only needs small bits of the major database in memory at one time. For example: a bank computer does not have all of it's millions of customer records in memory at the same time, it just loads the one customer record it needs while processing a transaction. Similarly inventory programs don't need to have the entire warehouse in memory, they just load the one item they need to work on....

    This strategy --called "Random Access Filing" is far simpler and often faster than linked lists because instead of having to sequence through entire lists to find things it can go directly to the disk record and load/save whatever it needs. It also lends itself to Binary Searching, which is a highly optimized means of finding information.

    Here is a demonstration program using Random Access Filing, copy-paste it, compile and run it to familiarize yourself with the file handling concepts...
    Code:
    //random access file demo
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
    #include <ctype.h>
    #include <string.h>
    
    #define FNAME "random.dat"
    
    // test data struct
    struct t_Record
      { int number;
        char word[16]; }
      Record;
    
    
    
    ///////////////////////////////////////////////////////
    // Random Access File Handlers
    //
    
    // open or create the file
    FILE *FileOpen(char* Filename)
      { FILE* pFile;
        pFile = fopen(Filename,"rb+");
        if (!pFile)
          pFile = fopen(Filename,"wb+");
        return pFile; }
    
    
    // Write a record to the file
    int WriteRecord(FILE *File, int RecNum)
      { if( fseek(File, RecNum * sizeof(Record), SEEK_SET) == 0 )
          if ( fwrite(&Record,sizeof(Record),1,File) )
            return 1;
        return 0; }
    
    
    // read a record from the file
    int ReadRecord(FILE *File, int RecNum)
      { if( fseek(File, RecNum * sizeof(Record), SEEK_SET) == 0 )
          if ( fread(&Record,sizeof(Record),1,File) )
            return 1;
        return 0; }
    
    
    int AddRecord(FILE *File)
      { fseek(File,0,SEEK_END);
        fwrite(&Record,sizeof(Record),1,File);
        return (ftell(File) / sizeof(Record)) - 1; }
    
    
    
    //////////////////////////////////////////////////////////////
    // View a Record
    //
    
    int ViewRecord (FILE *File, int RecNum)
      { if (! ReadRecord(File,RecNum))
          { printf("Invalid record\n"); 
            return -1; }
        printf("-----\n");
        printf("Record        : %d\n",RecNum);
        printf("Number Value  : %d\n",Record.number);
        printf("Word Value    : %s\n",Record.word);
        printf("-----\n");  
        return RecNum; }
    
    
    
    //////////////////////////////////////////////////////////////
    // Add a new record
    //
    
    int AddNewData(FILE *File)
      { memset(&Record,0,sizeof(Record));
        printf("\nEnter a number : ");
        scanf("%d", &Record.number);
        printf("Enter a word : ");
        scanf(" %s",Record.word);
        return AddRecord(File); }
    
    
    
    //////////////////////////////////////////////////////////////
    // Edit a record
    //
    
    int EditRecord(FILE *File, int RecNum)
      { if (! ReadRecord(File,RecNum))
          { printf("Invalid record\n");  
            return -1; }
        printf("\n-----\n");
        printf("Record        : %d\n",RecNum);
        printf("Number Value  : %d\n",Record.number);
        printf("Word Value    : %s\n",Record.word);
        printf("-----\n");  
        
        do
          { while(getchar() != '\n');
            printf("Change Values: Number, Word or Save (N, W or S) ? ");
            switch (toupper(getchar()))
              { case 'N' :
                  printf("\nEnter new number : ");
                  scanf("%d",&Record.number);
                  break;
                case 'W' : 
                  printf("Enter new word : ");
                  scanf(" %15s", Record.word);
                  break;
                case 'S' :
                  if (WriteRecord(File,RecNum))
                    printf("\nRecord #%d updated\n",RecNum);
                  return RecNum; } }
        while(1);
        return -1; }
    
    
    ////////////////////////////////////////////////////////////////
    // List records
    // 
    
    void ListRecords(FILE *File )
      { int i = 0;
        printf("\nRecord     Number\tWord\n\n");
        while (ReadRecord(File,i))
          { printf("%3d%16d\t%s\n",i,Record.number,Record.word); 
            i++; }
        printf("\n\n"); }
    
    
    
    ////////////////////////////////////////////////////////
    // this is for demonstration purposes only
    // you would not do this in a real program
    void InitFile(FILE* File)
     { int x, y;
       memset(&Record,sizeof(Record),0);
       for (x = 0; x < 10; x++)
          { Record.number = rand();
            for (y = 0; y < ((Record.number % 15) + 1); y++)
              Record.word[y] = (rand() % 26) + 'a';
            Record.word[y] = 0;
            if (! WriteRecord(File,x))
              printf("Oh drat!");  } }
     
    
    
    //////////////////////////////////////////////////////////
    // program mains
    //
    int main (void)
      { int Rec = 0; // record number
        FILE *File;
    
        srand(time(NULL));
    
        File = FileOpen(FNAME); 
        if (!File)
          { printf("Curses foiled again!\n\n");
            exit(-1); }
    
        printf("Random Access File Demonstration\n\n");
     
        do
          { printf("Menu : Dummy, Add, Edit, View, List, Quit (D, A, E, V, L or Q) : ");
            switch(toupper(getchar()))
              { case 'D' :
                  printf("Creating dummy file of 10 entries\n");
                  InitFile(File);
                  break;
                case 'A' :
                  Rec = AddNewData(File);
                  printf("Record #%d Added\n\n", Rec);
                  break;              
                case 'E' :
                  printf("\nRecord number (-1 Cancels): ");
                  scanf("%d",&Rec);
                  if (Rec > -1)
                    EditRecord(File,Rec);
                  break;
                case 'V' :
                  printf("\nRecord number (-1 Cancels): ");
                  scanf("%d",&Rec);
                  if (Rec > -1)
                    ViewRecord(File,Rec);
                  break;
                case 'L' :
                  ListRecords(File);
                  break;
                case 'Q' :
                  fclose(File);
                  return 0; } 
                  
             while(getchar() != '\n'); }
        while (1); 
        return 0; }

  5. #5
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by drew99 View Post
    If I wanted to write directly any structure to a file without putting all the nodes in a vector?
    WRT C/C++, we use the term array, as a vector is a C++ STL container type (using one of those will not benefit serialization, but an array will).

    If by "any structure" you mean a single node, yes, but it can't contain any pointers (such as "next_node"). If by "all the nodes" you mean, all the nodes, you don't actually have to put them into an array, you could just write them out one at a time. The reason I used an array in that article is because linked lists by definition need some tweaking before they are written out anyway (eg, to get rid of the next node pointer), and as mentioned, "for a demonstration of general purpose principles".

    So you want some combination of the array2list() and tofile() functions, but you do not have to incorporate all aspects. You can just walk the list, writing out each node in order, sans the next node pointer, and if there are other pointers in the node struct, taking those into account.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  6. #6
    Registered User
    Join Date
    Sep 2011
    Posts
    78
    I've tried to write every items of the structures. This is the code:

    Code:
    struct comp
    {
        int code;
        char name[25];
        char des[100];
        int disp;
        struct comp *n;
    };
    
    struct comp *comps = 0;
    int totc = 0;
    
    void read()
    {
        char *path = malloc(sizeof(char) * 50);
        int ch, i = 0;
        FILE *fr;
        struct comp *p, *c, *nn;
    
        fprintf(stderr, "\nInserisci il percorso e il nome del file: ");
    
        fflush(stdin);
        while((ch = getc(stdin)) != '\n' && i < 256)
        {
            path[i++] = ch;
        }
        path[i] = '\0';
    
        totc = 0;
        if(fr = fopen(path, "r"))
        {
            int ch, i = 0;
    
            //------------------------------------------------
            p = 0;
            c = comps;
            char v[10];
    
            fscanf(fr, "%i%*c", nn->code);
    
            ch = getc(fr);
            while(ch != '\n' && ch != 1)
            {
                nn->name[i++] = ch;
                ch = getc(fr);
            }
            nn->name[i] = '\0';
    
            ch = getc(fr);
            while(ch != '\n' && ch != 1)
            {
                nn->des[i++] = ch;
                ch = getc(fr);
            }
            nn->des[i] = '\0';
    
            fscanf(fr, "%i%*c", &nn->disp);
    
            nn->n = c;
            comps = nn;
            totc++;
            //------------------------------------------------
    
            while(feof(fr) == 0)
            {
                fscanf(fr, "%i%*c", &nn->code);
    
                ch = getc(fr);
                while(ch != '\n' && ch != 1)
                {
                    nn->name[i++] = ch;
                    ch = getc(fr);
                }
                nn->name[i] = '\0';
    
                ch = getchar();
                while(ch != '\n' && ch != 1)
                {
                    nn->des[i++] = ch;
                    ch = getc(fr);
                }
                nn->des[i] = '\0';
    
                fscanf(fr, "%i%*c", &nn->disp);
    
                nn->n = c;
                p->n = nn;
                totc++;
            }
        }
        fclose(fr);
        fflush(NULL);
    }
    
    void write()
    {
    char *path = malloc(sizeof(char) * 50);
        int ch, i = 0;
        FILE *fw;
        struct comp *p;
    
        fprintf(stderr, "\nInserisci il percorso e il nome del file: ");
    
        fflush(stdin);
        while((ch = getc(stdin)) != '\n' && i < 256)
        {
            path[i++] = ch;
        }
        path[i] = '\0';
    
        if(fw = fopen(path, "w"))
        {
            for(p = comps; p; p = p->n)
            {
                fwrite(&p->code, sizeof(int), 1, fw);
                fputc(1, fw);
                fwrite(p->name, sizeof(char), strlen(p->name), fw);
                fputc(1, fw);;
                fwrite(p->des, sizeof(char), strlen(p->des), fw);
                fputc(1, fw);
                fwrite(&p->disp, sizeof(int), 1, fw);
                fputc('\n', fw);
            }
        }
        fclose(fw);
        fflush(NULL);
    }
    It returns error in read();

  7. #7
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Why don't you just write the structure itself to disk and read it back as a struct...
    Code:
    fwrite(&nn, sizeof(nn), 1, fw);  
    
    fread(&nn, sizeof(nn), 1, fr);
    Just do a fixup on the pointer values as you reload them.

    Easy as pie...

    Also, you malloced only 50 characters for path, but you allow your user to enter up to 256, which is a pretty significant memory overrun.
    Last edited by CommonTater; 11-21-2011 at 09:40 AM.

  8. #8
    Registered User
    Join Date
    Sep 2011
    Posts
    78
    I've decided to not save the complete structure, because it is not said that name and d are always long, respectively, 25 and 100, so I can save space. I corrected the error about path.

  9. #9
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by drew99 View Post
    I've decided to not save the complete structure, because it is not said that name and d are always long, respectively, 25 and 100, so I can save space. I corrected the error about path.
    Unless you are trying to stuff this onto a floppy disk, I can't imagine space being an issue. Your struct is ~134 bytes... you're going to get 7,800+ into a megabyte. Is saving a little bit of space worth all that extra work?

    It's all the character by character loops you have going on that are causing your problems... If you don't want to do it the simple way use fprintf() to write, fscanf() or fgets() to read ... it'll save you a hundred nasty headaches.
    Last edited by CommonTater; 11-21-2011 at 09:56 AM.

  10. #10
    Registered User
    Join Date
    Sep 2011
    Posts
    78
    The function fscanf does not read spaces: a name or a description of a component can contain spaces.

  11. #11
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by drew99 View Post
    The function fscanf does not read spaces: a name or a description of a component can contain spaces.
    It depends how you use it.

    Code:
    	char eg[]="some item 321", buf[32];
    	sscanf(eg, "%[a-z A-Z]", buf);
    	printf("->%s<-", buf);
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  12. #12
    Registered User
    Join Date
    Sep 2011
    Posts
    78
    I solved. Here is the complete code:

    Code:
    struct comp
    {
        int code;
        char name[27];
        char des[102];
        int disp;
        struct comp *n;
    };
    
    struct comp *comps = 0;
    
    void apri()
    {
        char *path = malloc(sizeof(char) * 256);
        int ch, i = 0;
        FILE *fr;
        struct comp *nn = calloc(1, sizeof(struct comp)), *p = 0;
    
        fputs("\nInserisci il percorso e il nome del file: ", stderr);
    
        fflush(stdin);
        while((ch = getc(stdin)) != '\n' && i < 256)
        {
            path[i++] = ch;
        }
        path[i] = '\0';
    
        if((fr = fopen(path, "rb")) != NULL)
        {
            comps = nn;
    
            //-----------------------------------------------------------------   prima struttura
            fread(&nn->code, sizeof(int), 1, fr);
    
            i = 0;
            ch = getc(fr);
            do
            {
                nn->name[i++] = ch;
                ch = getc(fr);
            }
            while(ch != '\0');
    
            i = 0;
            ch = getc(fr);
            do
            {
                nn->des[i++] = ch;
                ch = getc(fr);
            }
            while(ch != '\0');
    
            fread(&nn->disp, sizeof(int), 1, fr);
    
            getc(fr);
    
            p = nn;
            nn = calloc(1, sizeof(struct comp));
            //-----------------------------------------------------------------   fine prima struttura
    
            while((ch = getc(fr)) && (!feof(fr)) && ungetc(ch, fr))
            {
                fread(&nn->code, sizeof(int), 1, fr);
    
                i = 0;
                ch = getc(fr);
                do
                {
                    nn->name[i++] = ch;
                    ch = getc(fr);
                }
                while(ch != '\0');
    
                i = 0;
                ch = getc(fr);
                do
                {
                    nn->des[i++] = ch;
                    ch = getc(fr);
                }
                while(ch != '\0');
    
                fread(&nn->disp, sizeof(int), 1, fr);
    
                getc(fr);
    
                p->n = nn;
                p = nn;
                nn = calloc(1, sizeof(struct comp));
            }
            p->n = 0;
        }
    
        fclose(fr);
        free(path);
        free(nn);
    }
    
    void salva()
    {
        char *path = malloc(sizeof(char) * 256);
        int ch, i = 0;
        FILE *fw;
        struct comp *p;
    
        fputs("\nInserisci il percorso e il nome del file: ", stderr);
    
        fflush(stdin);
        while((ch = getc(stdin)) != '\n' && i < 256)
        {
            path[i++] = ch;
        }
        path[i] = '\0';
    
        if((fw = fopen(path, "wb")) != NULL)
        {
            for(p = comps; p; p = p->n)
            {
                fwrite(&p->code, sizeof(int), 1, fw);
                fwrite(p->name, sizeof(char), strlen(p->name), fw);
                fputc('\0', fw);
                fwrite(p->des, sizeof(char), strlen(p->des), fw);
                fputc('\0', fw);
                fwrite(&p->disp, sizeof(int), 1, fw);
                fputc('\n', fw);
            }
        }
    
        fclose(fw);
        free(path);
    }
    Thanks to all for your help.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Structures - Linked List
    By Oonej in forum C Programming
    Replies: 14
    Last Post: 07-14-2011, 09:54 PM
  2. i want write contact records with linked list
    By captain_turkiye in forum C Programming
    Replies: 72
    Last Post: 05-15-2011, 05:17 PM
  3. Structure of structures + linked list
    By saeculum in forum C Programming
    Replies: 3
    Last Post: 03-06-2009, 08:02 PM
  4. read/write linked list
    By lambs4 in forum C Programming
    Replies: 4
    Last Post: 03-29-2003, 06:38 PM
  5. problem with structures and linked list
    By Gkitty in forum C Programming
    Replies: 6
    Last Post: 12-12-2002, 06:40 PM