Read file into typedef struct that includes an enum in C?

Viewed 344

I am having trouble reading in a file into a typedef struct that includes an enum. I am a beginner in C, so I don't really know how to read in the file with the enum.

I am able to read the file and print out the contents in simple code, but I need to read the file in and assign each string in the line into the type from the typedef struct.

The input file ooks like this:

random.num year model category
Example:
54 2012 model1 type1

These are the relevant parts of my code:

typedef enum { type1, type2, type3, type4} category;

typedef struct {
        int num;
        int year;
        char make[MAX_MAKE_CHARS];
        category category;  //enum from above
}item; 

//reading in the file I have this:

int create_db(char *f){

char file_contents[100];                                                                                                               // read the file --> ("r")                                                                                                             FILE *f = fopen(f, "r");
                                                                                                                                               // check if file can be used
if(f == NULL){ 
      printf("File not found");
      exit(1);
}
                                                                                                                                               int i = 0;                                                                                                                             item item_array[100];
while(fscanf(f, "%d %d %s %s", &item[i].num, &item[i].year, item[i].make, item[i].category) != EOF){ 
         // can't get past the while look where it says "item[i].category

I get the error:

format ‘%s’ expects argument of type ‘char *’, but argument 6 has type ‘category * {aka enum *}’

Since I am completely new to C, I am confused on how to read in the file into a struct. What do I do for the item[i].category?

2 Answers

Unfortunately an enum is a symbol that is valid only in the code, and there's no way to access this symbol at runtime as a string. Any enum variable is actually stored as an integer

But there is something you can do:

  • Define an array of char * containing the symbols of the enum as strings
  • Store the enum symbol contained in the input file within a temporary string
  • Check the return value of scanf
  • Call an utility function searching the temporary string within the constant array of strings and returning the corresponding enum value
  • If the string is not found raise an error
  • If the string is found, store the enum value within your output struct

In the following example code I omit your while for clarity (you will be able to add it back according to your requirements not described in the question):

int tmp_num;
int tmp_year;
char tmp_make[100];
char tmp_category_str[10]; // <-- define the size according to the max enum symbol length

if(fscanf(f, "%d %d %99s %9s", &tmp_num, &tmp_year, tmp_make, tmp_category_str) == 4)
{
    int tmp_category;
    if( ( tmp_category = check_enum( tmp_category_str ) ) != -1 )
    {
        Item.num  = tmp_num;
        Item.year = tmp_year;
        strcpy( make, tmp_make );
        Item.category = ( category )tmp_category; // The numeric value is stored in the struct
    }
    else
    {
        printf( "Invalid 'category' value!\n" );
    }
}

Note that:

  • I expect the return value of fscanf to be 4
  • I changed make size to 100. The purpose is showing in a simple way that the format specifier %s should contain the limit to arr_size-1 characters, for safety
  • The check_enum() will return -1 if a strange string value, not matching any enum symbol, is provided

Now, only check_enum() implementation is missing. Just loop on enumSymbols[] elements searching for the input string.

char* enumSymbols[] = { "type1", "type2", "type3", "type4" };

int check_enum(char * str)
{
    int ret = -1;
    
    if( str )
    {
        for( int i=0; i<(sizeof(enumSymbols)/sizeof(enumSymbols[0])); i++ )
        {
          if( strcmp(enumSymbols[i], str) == 0 )
          {
              ret = i;
              break;
          }
        }
    }

    return ret;
}

Note: this works but you'll have to keep the array and the enum aligned. In order to prevent human alignment mistakes, a solution like this one can be implemented. In this answer I explain how to create an aligned enum/struct pair, but in the same way and using the preprocessor stringifier operator # also an aligned enum/string array pair can be generated.

Roberto's answer is nice and contains the most crucial information:

Any enum variable is actually stored as an integer

If you are able to change the input format from a string representation of the enum to an integer (i.e. 0 instead of type1 in the input), you can directly read it using "%d" in fscanf.

Related