Как получить 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") отлично работает для строкового литерала, но имена в файле ранее не известны, поэтому размер должен определяться динамически.


  1. использование fgets() читать строку.

  2. использование 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. Код достаточно прост: пример реализации

Преобразование комментария в ответ.

У вас есть как минимум два варианта.

  1. Самый портативный:

     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() не делает.

  2. Использование функций 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() выделяет необходимую память для вас.

Вы должны освободить выделенную память для каждого имени позже, конечно, какое бы решение вы не использовали.

Другие вопросы по тегам