2016-12-16 7 views
1

Давайте предположим, что у нас есть файл, который содержит:Как получить 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); 

Хорошо, это работает, но как выделить память для каждого имени, прежде чем получить его из текстового файла? Я решил использовать массив структур, потому что я буду использовать его для реализации хэш-таблицы.

+0

Вы не можете выделить _before_, так как вы не знаете размер. 'strlen' каждое из ваших имен из файла, а затем' malloc' – mihai

+1

У вас есть как минимум два варианта. (1) Большинство портативных: 'char buffer [1024]; if (fscanf (fp, "% d% 1023s", & prt [0] .n, buffer) == 2) {prt [0] .name = strdup (buffer); ... использовать значения после проверки работы strdup ...} else {... handle error ...}}, который использует большой буфер для чтения значения, а затем выделяет память впоследствии; или (2) Использование функций POSIX в ['fscanf()'] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/fscanf.html): 'if (fscanf (fp,"% d% ms " , & prt.n, & prt [0] .name) == 2) {... использовать значения read ...} else {... handle error ...} '. Обратите внимание на ограничение размера для первого вызова. –

+0

Вызов 'fscanf()' не должен компилироваться - вы пропустили аргумент указателя файла. –

ответ

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. Достаточно легко ввести код: Example implementation

+0

Дополнительная проверка ошибок может быть выполнена с 'if (record-> name == NULL)' после выделения и обрезки любого лишнего белого пробела в конце имени. – chux

0

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

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

  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 и не является частью стандартного С. Легко писать, хотя:

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^