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