Nobody Noname
Nobody Noname

Reputation: 91

How to get int and string from file and save it in structure?

Let's assume that we have file which contains:

 1 John 
 2 Alex 
 3 Michael 

We can get one line using fscanf() function, but how to save it to below structure:

 typedef struct entry { 
 int n; 
 char *name; 
 } entry_t; 

I want to create the array of structures and save values from file to it, and do it dynamically. I've tried doing this in that way

entry_t *prt = malloc ( size * sizof(entry_t) ); 
//opening file 
prt[0].name = malloc ( sizeof("John") ); 
fscanf (fp,"%d %s", prt[0].n, prt[0].name); 

Ok, it works, but how to allocate memory for every name before get it from text file? I decided to use array of structures, because I'll use it to implement hash table.

Upvotes: 1

Views: 164

Answers (2)

Jonathan Leffler
Jonathan Leffler

Reputation: 754560

Converting comment into answer.

You have at least two options.

  1. Most portable:

     char buffer[1024];
     if (fscanf(fp, "%d %1023s", &prt[0].n, buffer) != 2)
         …handle I/O (format?) error…
     else if ((prt[0].name = strdup(buffer)) == 0)
         …handle out-of-memory error…
     else
     {
         …use values, or continue loop…
     }
    

    This uses a large buffer to read the value and then allocates the appropriate memory for use in the structure afterwards. Note the overflow protection in the argument (and the difference by one is necessary). Note that strdup() is part of POSIX and not part of standard C. It is easy to write, though:

    char *strdup(const char *str)
    {
        size_t len = strlen(str) + 1;
        char *copy = malloc(len);
        if (copy != 0)
            memmove(copy, str, len);
        return copy;
    }
    

    There'll be the usual debate of memmove() vs memcpy(); both work in this context, but memmove() works everywhere and memcpy() doesn't.

  2. Using POSIX features of fscanf():

    if (fscanf(fp, "%d %ms", &prt.n, &prt[0].name) != 2)
        …handle I/O (format?) error…
    else
    {
        …use values, or continue loop…
    }
    

    Note that in this context, you do pass the address of the pointer prt[0].name as fscanf() allocates the necessary memory for you.

You need to free the allocated memory for each name later, of course, whichever solution you're using.

Upvotes: 0

chux
chux

Reputation: 154255

sizeof("John") works fine for a string literal, but the names in the file are not known prior, so size must be determined dynamically.


  1. Use fgets() to read a line.

  2. Use sscanf(), strtol(), strtok() to parse that line.

Example:

int read_entry(FILE *istream, struct entry *record) {
  char buf[200];
  if (fgets(buf, sizeof buf, istream) == NULL) return -1;  // EOF
  buf[strcspn(buf, "\n")] = 0;  // lop off potential trailing \n

  int start;
  int end = 0;
  sscanf(buf, "%d %n%*s%n", &record->n, &start, &end); 

  if (end == 0) {
    return 0;  // failed to parse
  }
  record->name = strdup(&buf[start]);
  return 1; // Success
} 

Usage

struct entry record;
while (read_entry(stdin, &record) == 1) {
  printf("%d '%s'\n", record.n, record.name);
  ...
  // when done with the record,
  free(record.name);
}

strdup() is a common way to "duplicate" a string, yet it is not part of the standard C library. Easy enough to code: Example implementation

Upvotes: 2

Related Questions