Reputation: 91
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
Reputation: 754560
Converting comment into answer.
You have at least two options.
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.
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
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.
Use fgets()
to read a line.
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