user4054198
user4054198

Reputation:

Return a value inside and outside the while loop in C

I want to write a program which reads a file and breaks down its contents. The file basically contains a string containing IDs. The contents of the file are like this:

[root@ben-pc ~]# cat file
1,2,3

I want to get the IDs 1,2,3 seperately returned from a parsing function. In my code i get the first two IDs within the while loop and the last ID outside the while loop. But I am not able to return all these values properly. I know I cannot use two return calls in the code like this. Can any one suggest how can I get this working as desired. Here is my code:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>

char *get_group_members (char* file)
{
    FILE *fp;
    char *str;
    char filename[64];
    int c;
    size_t n = 0;

    snprintf(filename, sizeof(char) * 64, "/home/ben/%s", file);
    fp = fopen(filename, "r");

    if(fp == NULL)
    {
            perror("Error opening file");
    }

    str = malloc(100);

    while ((c = fgetc(fp)) != EOF )
    {
            if(c != ',')
            {
                    str[n++] = (char) c;
            }
            else
            {
                    //return str; Not working
                    printf("%s,", str);
                    n = 0;
            }
    }
    //return str; Not working
    printf("%s", str);
    fclose(fp);
}

int main (void)
{
printf("Group Members are ::\n");
printf("%s\n", get_group_members("file"));
return 0;
}

Upvotes: 0

Views: 1880

Answers (1)

dom0
dom0

Reputation: 7486

Two main options here: Either return an array of strings, or do incremental parsing.

  • Incremental parsing: Instead of opening the file in your function you pass it an already opened FILE*. The function then returns the next member (as a single string), or 0, if there are no more. fopen/fclose must be managed by the calling function, usage could look like this (error checks omitted):

    FILE *fd = fopen(...);
    char *member;
    while((member = get_member(fd))) {
        // do sth.
    
        free(member);
    }
    fclose(fd);
    

    The nice thing about incremental parsing is that you never need to store all returned data in memory at once and you neither need to know the number of elements to return beforehand (to allocate storage for them) nor need to handle reallocs if you read more elements than expected.

    Sample implementation:

    char *get_group_member (FILE *fp)
    {
        char *str = malloc(100);
        int n = 0;
        int c;
    
        while ((c = fgetc(fp)) != EOF)
        {
            if(c != ',')
            {
                str[n++] = (char) c;
            }
            else
            {
                break;
            }
        }
    
        if(n)
        {
            str[n++] = 0;
            return str;
        }
    
        free(str);
        return 0;
    }
    

    Note: This implementation, like your original implementation, suffers a buffer overflow if any single member is longer than 99 characters.

  • Array of strings: Either by returning a char **, whose size is numberOfItems + 1 and ends with a 0.

    Iteration is easy over such an array and looks about like this:

    char ** result = get_members...;
    char *current_member;
    
    while((current_member = result++)) {
        // do something with current_member
    }
    
  • Or by using two out-parameters, like this:

    void get_members_...(char ** result, int *result_size);

    You then allocate an array of appropriate size and set result_size accordingly.

  • You could also create something like a linked list, but that makes using the result more involved and requires generally more code.

Upvotes: 1

Related Questions