Как получить int и строку из файла и сохранить его в структуре?
Давайте предположим, что у нас есть файл, который содержит:
1 John
2 Alex
3 Michael
Мы можем получить одну строку, используя fscanf()
функция, но как сохранить ее в структуре ниже:
typedef struct entry {
int n;
char *name;
} entry_t;
Я хочу создать массив структур и сохранить значения из файла в него, и сделать это динамически. Я пытался сделать это таким образом
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);
Хорошо, это работает, но как выделить память для каждого имени, прежде чем получить его из текстового файла? Я решил использовать массив структур, потому что я буду использовать его для реализации хеш-таблицы.
2 ответа
sizeof("John")
отлично работает для строкового литерала, но имена в файле ранее не известны, поэтому размер должен определяться динамически.
использование
fgets()
читать строку.использование
sscanf()
,strtol()
,strtok()
разобрать эту строку.
Пример:
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
}
использование
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()
это обычный способ "продублировать" строку, но она не является частью стандартной библиотеки C. Код достаточно прост: пример реализации
Преобразование комментария в ответ.
У вас есть как минимум два варианта.
Самый портативный:
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… }
При этом для чтения значения используется большой буфер, а затем выделяется соответствующая память для использования в структуре. Обратите внимание на защиту от переполнения в аргументе (и разница на единицу необходима). Обратите внимание, что
strdup()
является частью POSIX, а не частью стандартного C. Это легко написать, хотя:char *strdup(const char *str) { size_t len = strlen(str) + 1; char *copy = malloc(len); if (copy != 0) memmove(copy, str, len); return copy; }
Там будут обычные дебаты
memmove()
противmemcpy()
; оба работают в этом контексте, ноmemmove()
работает везде иmemcpy()
не делает.Использование функций POSIX
fscanf()
:if (fscanf(fp, "%d %ms", &prt.n, &prt[0].name) != 2) …handle I/O (format?) error… else { …use values, or continue loop… }
Обратите внимание, что в этом контексте вы передаете адрес указателя
prt[0].name
какfscanf()
выделяет необходимую память для вас.
Вы должны освободить выделенную память для каждого имени позже, конечно, какое бы решение вы не использовали.