Thread: Struct Help

  1. #1
    Registered User
    Join Date
    Oct 2012
    Posts
    126

    Struct Help

    Alright I don't understand how I would go about reading in from a file to store those values in multiple structs. I've been trying to read tutorials on this for awhile to no avail. Could someone shed some light on this for me in lame mans terms would be helpful haha. This is what I'm trying to do

    Code:
    #include <stdio.h>
    
     typedef struct {
              int number;
              int rating;
              char compName;
              char compType;
              
      
      } review;
    int main()
    {
    int i;
    char buffer[1000];
    
    FILE *fp; //setting file pointer to fp and reading the file
    
        fp=fopen("review.txt", "r");
                while (fgets(buffer,1000,fp)!= NULL){ //storing the file in buffer 
                puts(buffer);
                }
                
        //Now I want to store the multiple values that are in the text value into the multiple elements of the structure, theres going to be 4 values for number etc, they are formatted line by line in the file
    
    }
    Not sure how to go about it at all, reading material, examples, anything would be appreciated.
    Last edited by Sorinx; 12-04-2012 at 11:24 PM.

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    Lets start with assuming there are 10 lines in the file, constituting 10 records of data.

    So
    Code:
    review mydata[10];
    Having read a line, we do
    Code:
    if ( sscanf(buffer,"%d %d %c %c", &mydata[n].number, &mydata[n].rating, 
                &mydata[n].compName, &mydata[n].compType ) == 4 ) {
        // successfully read the data, increment count
        n++;
    } else {
        // some error - complain, ignore, quit?
    }
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Oct 2012
    Posts
    126
    My problem is I don't want to assume anything about the length of data in the file because I want to be able to give the user option to input the data, I really appreciate what you have shown me so far, as it helps certainly. The first part is for a homework assignment, which you certainly gave me an understanding as I was going to do it a lot harder way lol, however what I'm asking now is just so I can figure out how I can use this to write a n amount of structures to represent information I want to add to, for my own knowledge
    Last edited by Sorinx; 12-05-2012 at 09:41 AM.

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    Get a fixed length working first.
    It's then relatively easy to extend that to cope with variable length files.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User
    Join Date
    Oct 2012
    Posts
    126
    Any suggestions on reading materials? Everything I have found on structures was rather basic, even the book I have does go in depth about them, and I understand the basic premise, it's the intermediary stuff like this that I would like to understand better.

  6. #6
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    Linked Lists in C Tutorial - Cprogramming.com

    This topic really helped me go beyond the basics of structures, and it looks like it might work well for your needs.

  7. #7
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    The dynamic version of an array is malloc.

    The extendible and dynamic version of an array is realloc.

    There are plenty of examples on the board showing how to use malloc and realloc to allocate memory to mimic a variable (and expanding) array.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  8. #8
    Registered User
    Join Date
    Oct 2012
    Posts
    126
    Can I do something like this?

    Code:
    #include <stdio.h>
     
     typedef struct {
                      
              char compName[50];
              char compType[50];
              float price;
              int rating;
               
      }review;
     
    int main()
    {
    int i, n=0, size;
    char buffer[1000];
    review mydata[10];
     
    FILE *fp;
    
    fp=fopen("review.txt", "r");
    
        for(n=0; n<10 && n != '\0'; n++);{
            fscanf(fp, "%s, %s, %s, %s",&mydata[n].compName, &mydata[n].compType,
                    &mydata[n].price, &mydata[n].rating);}
    
    
     
     fclose(fp);
     }

  9. #9
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    I'll happily throw you in the deep end, Sorinx.

    Note that compName and compType were single characters in your structure. I'm assuming you want them as dynamically allocated strings.
    Code:
    #include <stdlib.h>
    #include <sys/types.h>
    #include <string.h>
    #include <stdio.h>
    #include <errno.h>
    
    /* If defined, this program will accept any newline convention in input.
     * Use "b" flag when opening the input streams. */
    #define ANY_NEWLINE
    
    /* If defined, this program uses horizontal tabs, '\t', to separate the two strings
     * on each record. */
    #define TAB_SEPARATOR
    
    typedef struct {
        int      number;
        int      rating;
        char    *comp_name;
        char    *comp_type;
    } review_t;
    
    
    /* A C89 implementation of POSIX.1-2008 getline() function.
     * See http://www.kernel.org/doc/man-pages/online/pages/man3/getline.3.html
    */
    static ssize_t getline(char **const lineptr, size_t *const sizeptr, FILE *const in)
    {
        char    *line, *temp;
        size_t   size;
        size_t   used = 0;
        int      c, saved_errno;
    
        if (!lineptr || !sizeptr || !in) {
            errno = EINVAL;
            return (ssize_t)-1;
        }
    
        if (*sizeptr > 0) {
            line = *lineptr;
            size = *sizeptr;
        } else {
            line = NULL;
            size = 0;
        }
    
        saved_errno = errno;
    
        while (1) {
    
            /* Need to grow the buffer? */
            if (used + 2 >= size) {
    
                /* Size policy: */
                if (used < 1024)
                    size = (used | 127) + 129;
                else
                if (used < 1048574)
                    size = (used * 5) / 4;
                else
                    size = (used | 131071) + 131073;
    
                /* Reallocate. */
                temp = realloc(line, size);
                if (!temp) {
                    if (line)
                        line[used] = '\0';
                    errno = ENOMEM;
                    return (ssize_t)-1;
                }
                line = temp;
    
                /* Update pointer and size. */
                *lineptr = line;
                *sizeptr = size;
            }
    
            /* Since fgets() does not work with embedded NULs,
             * we need to read input character by character. */
            c = getc(in);
    
            /* End of input? */
            if (c == EOF) {
                line[used] = '\0';
                if (used == 0) {
                    errno = 0;
                    return (ssize_t)-1;
                } else {
                    errno = saved_errno;
                    return (ssize_t)used;
                }
            }
    
            /* Add char to buffer. */
            line[used++] = c;
    
            /* Newline? */
    #ifdef ANY_NEWLINE
            if (c == '\n') {
    
                c = getc(in);
                if (c == '\r')
                    line[used++] = c;
                else
                if (c != EOF)
                    ungetc(c, in);
    
                line[used] = '\0';
                errno = saved_errno;
                return used;
    
            } else
            if (c == '\r') {
    
                c = getc(in);
                if (c == '\n')
                    line[used++] = c;
                else
                if (c != EOF)
                    ungetc(c, in);
    
                line[used] = '\0';
                errno = saved_errno;
                return used;
            }
    #else
            if (c == '\n') {
                line[used] = '\0';
                errno = saved_errno;
                return (ssize_t)used;
            }
    #endif
        }
    }
    
    
    /* A C89 implementation of POSIX.1-2008 strndup() function.
     * See http://www.kernel.org/doc/man-pages/online/pages/man3/strndup.3.html
    */
    static char *strndup(const char *const string, const size_t length)
    {
        char *result;
    
        result = malloc(length + 1);
        if (!result) {
            errno = ENOMEM;
            return NULL;
        }
    
        strncpy(result, string, length);
    
        result[length] = '\0';
    
        return result;
    }
    
    
    int main(void)
    {
        const char      *file_name = "review.txt";
        FILE            *file;
    
        char            *line_data = NULL;
        size_t           line_size = 0;
        ssize_t          line_len;
        unsigned long    line_number = 0UL;
    
        review_t        *review_array = NULL;
        size_t           review_count = 0;
        size_t           review_allocated = 0;
    
        int              number, rating;
        int              name_start, name_end, type_start, type_end;
    
        int              status;
        size_t           i;
    
    #ifdef ANY_NEWLINE
        file = fopen(file_name, "rb");
    #else
        file = fopen(file_name, "r");
    #endif
    
        if (!file) {
            fprintf(stderr, "%s: %s.\n", file_name, strerror(errno));
            return 1;
        }
    
        while (1) {
    
            line_len = getline(&line_data, &line_size, file);
            if (line_len < (ssize_t)1) {
    
                /* Error or end of file? */
                if (ferror(file) || !feof(file))
                    status = EIO;
                else
                    status = 0;
    
                /* Break out of while(1) loop. */
                break;
            }
    
            /* A new line was successfully read. */
            line_number++;
    
            /* Set last %n to -1, so we can detect whether it was parsed or not. */
            type_end = -1;
    
            /* Try parsing the input line. */
    #ifdef TAB_SEPARATOR
            if (sscanf(line_data, " %d %d %n%*[^\t\r\n]%n %n%*[^\t\r\n]%n",
    #else
            if (sscanf(line_data, " %d %d %n%*s%n %n%*s%n",
    #endif
                                  &number, &rating,
                                  &name_start, &name_end,
                                  &type_start, &type_end) < 2 || type_end == -1) {
                fprintf(stderr, "%s: Line %lu: Cannot parse line.\n", file_name, line_number);
                status = EINVAL;
                break;
            }
    
            /* Need to reallocate for more reviews? */
            if (review_count >= review_allocated) {
                review_t *temp;
                size_t    size = review_count + 256;
    
                temp = realloc(review_array, size * sizeof *review_array);
                if (!temp) {
                    fprintf(stderr, "%s: Line %lu: Out of memory.\n", file_name, line_number);
                    status = ENOMEM;
                    break;
                }
    
                review_array = temp;
                review_allocated = size;
            }
    
            /* Populate the entry. */
            review_array[review_count].number = number;
            review_array[review_count].rating = rating;
            review_array[review_count].comp_name = strndup(line_data + name_start, name_end - name_start);
            review_array[review_count].comp_type = strndup(line_data + type_start, type_end - type_start);
    
            if (!review_array[review_count].comp_name || !review_array[review_count].comp_type) {
                fprintf(stderr, "%s: Line %lu: Out of memory.\n", file_name, line_number);
                status = ENOMEM;
                break;
            }
    
            review_count++;
        }
    
        /* Discard the line buffer. */
        free(line_data);
        line_data = NULL;
        line_size = 0;
    
        /* Close the input file. */
        if (fclose(file))
            if (!status)
                status = EIO;
    
        /* We could abort here when status is nonzero, i.e.
        if (status)
            return 1;
           but for this example I'll let it continue even then. */
    
        if (review_count > 0) {
            printf("Read %lu records:\n", (unsigned long)review_count);
    
            for (i = 0; i < review_count; i++)
                printf("%7lu. number = %d, rating = %d, name = \"%s\", type = \"%s\".\n",
                       1UL + (unsigned long)i,
                       review_array[i].number,
                       review_array[i].rating,
                       review_array[i].comp_name,
                       review_array[i].comp_type);
    
        } else
            printf("No records read.\n");
    
        /* Being anal, we discard the arrays since they're no longer needed. */
        free(review_array);
        review_array = NULL;
        review_count = 0;
        review_allocated = 0;
    
        return 0;
    }
    I don't know where to start explaining it, and I'm really too lazy to go over it line by line, but if you have any specific spots you want me to explain (why the code does what it does, and how a specific part works), I'd be happy to try to.

    I did just whip it up from scratch, so it may contain bugs. If you or anyone else spots one, please do say so, preferably including the fixed code in your post.

    The main useful points in my mind, are
    • Dynamic memory allocation.
      In all cases there is the pointer, the currently used size, and the allocated size. (I often use size for the allocated size, and used for the length or count actually used.)
      • realloc(NULL, size) is the same as malloc(size)
      • free(NULL) is safe, and does nothing
    • Usefulness of POSIX.1-2008 strndup() and getline() functions
      I included C89-compatible implementations in case your C library does not provide them. In most current environments, if you #define _POSIX_C_SOURCE 200809L before any #includes, your C library should provide those for you.
    • How simple it would be to support any newline convention
      My implementation of getline() accepts \n, \r, \r\n, and \n\r as newlines.
      Unfortunately, the standard getline() does not.
    • Parsing input using complicated sscanf() patterns
      The main point, really, is that instead of copying substrings into separate arrays, you can use %n to save the position in the string (it takes a pointer to an integer). Unfortunately, the standards disagree whether %n is considered a conversion or not.
      As a solution, I initialize the integer matching the last %n to -1, which is a value scanf() will never assign to it. If it is nonnegative after the scanf() call, it was converted. Otherwise not.


    As to the C functions used, I warmly recommend looking at the Linux man-pages project. The section 3 contains the C interfaces. Although it is a Linux project, it is not Linux-specific; each man page has a Conforming to section, which tells you which standards or conventions it is described in.

  10. #10
    Registered User
    Join Date
    Oct 2012
    Posts
    126
    Yes that's definitely quite a bit over my head lol. I couldn't write anything like that as of yet. I understand the concepts, but I have a hard time implementing still

  11. #11
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    Code:
        for(n=0; n<10 && n != '\0'; n++);{
            fscanf(fp, "%s, %s, %s, %s",&mydata[n].compName, &mydata[n].compType,
                    &mydata[n].price, &mydata[n].rating);}
    Well the types of conversions don't all match the members of the struct. Might want to give this thread a read: https://cboard.cprogramming.com/showthread.php?t=13400, while it's not an exhaustive reference it should keep you from making the same mistakes.

    It is also helpful to remember and take advantage of the fact that *scanf functions return a number equal to the things they converted, so you can handle input errors somewhat.

    I'm surprised to see something like n < 10 && n != '\0' in the loop. What do you think that is doing? I don't think a comparison with '\0' is at all relevant. It just confuses your logic.

  12. #12
    Registered User
    Join Date
    Oct 2012
    Posts
    126
    Yeah idk what I was doing, trying to get cute I guess. I'm just going to do it the easiest way

    Code:
    fscanf(fp,"%d", &size);
    
    for(n=0;n<10;n++){
    fscanf(fp,"%s%s%f%d", &mydata[n].compName, &mydata[n].compType,
                    &mydata[n].price, &mydata[n].rating);}
     fclose(fp);
    Now I need to malloc the size of the struct based on the number in the file that I took out and stored in size
    Last edited by Sorinx; 12-06-2012 at 10:26 PM.

  13. #13
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > for(n=0; n<10 && n != '\0'; n++);
    This loop does nothing for two different reasons.
    1. There is a ; at the end
    2. You set n to 0, then one of the conditions is that n is NOT 0.

    All you had to do was take the while(fgets loop in your post #1, and combine it with the sscanf code in my post #2.

    Until you manage this, there's no point worrying about any kind of dynamic memory.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  14. #14
    Registered User
    Join Date
    Oct 2012
    Posts
    126
    Quote Originally Posted by Salem View Post
    > for(n=0; n<10 && n != '\0'; n++);
    This loop does nothing for two different reasons.
    1. There is a ; at the end
    2. You set n to 0, then one of the conditions is that n is NOT 0.

    All you had to do was take the while(fgets loop in your post #1, and combine it with the sscanf code in my post #2.

    Until you manage this, there's no point worrying about any kind of dynamic memory.
    Passing it to an array first is kind of pointless for this so I took that part out

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Sorting array of struct inside struct
    By blueboyz in forum C Programming
    Replies: 13
    Last Post: 04-24-2012, 02:15 AM
  2. struct within a struct, odd behavior accessing variables
    By John Gaden in forum C++ Programming
    Replies: 2
    Last Post: 02-28-2012, 06:19 AM
  3. Using data struct and I got an error in struct function
    By abrofunky in forum C++ Programming
    Replies: 4
    Last Post: 02-18-2012, 07:47 PM
  4. Replies: 1
    Last Post: 05-12-2011, 01:02 AM
  5. struct holding data inside a linked list struct
    By icestorm in forum C Programming
    Replies: 2
    Last Post: 10-06-2009, 12:49 PM