C установка IDE C структура програми C змінні C типи даних С функція printf C константи C арифметичні операції C операції порівняння і логічні операції C порозрядні операції C операції присвоювання C перетворення типів C умовні конструкції C цикли С масиви і рядки С функція scanf C препроцесор. Директива #include C #define директива C макроси C умовна компіляція C функції. Визначення та опис функцій C функції. Передача параметрів в функцію C функції. Повернення результату з функції C функції. Рекурсивні функції C область видимості змінних C зовнішні об'єкти C вказівники C вказівник. Операції з вказівниками C покажчики. Арифметика покажчиків C покажчики. Константи і покажчики C покажчики. Покажчики та масиви C покажчики. Масиви покажчиків, рядки і багаторівнева адресація C покажчики. Покажчики в параметрах функції C покажчики. Динамічна пам'ять C покажчики. Покажчик як результат функції C покажчики. Управління динамічної пам'яттю C покажчики. Покажчики на функцію C покажчики. Покажчики на функції як параметри і результати функцій C покажчики. Функції зі змінною кількістю параметрів C struct. Визначення структур C struct. Структури як елементи структур C struct. Покажчики на структури C struct. Масиви структур C struct. Структури і функції C struct. union об'єднання C struct. Бітові поля С file. Введення-виведення і робота з файлами C file. Читання і запис бінарних файлів C file. Читання і запис структур в файл C file. Читання і запис текстових файлів C file. Форматування вводу-виводу C file. Позиціонування в потоці C file. Консольне введення-виведення

C file. Читання і запис структур в файл


Хоча функції getc () / putc () дозволяють вносити в файл окремі символи, але фактично ми маємо справу з бінарними файлами. Якщо ми записуємо в файл рядок, то в принципі ми навіть можемо відкрити записаний файл будь-якому текстовому редакторі і зрозуміти, що там було записано. Але не завжди дані можуть представляти рядка. І щоб більш наочно розібратися з роботою з бінарними файлами, розглянемо ще один приклад - із записом-читанням структури з файлу:

#include <stdio.h>
#include <stdlib.h>
 
struct person
{
    char name[16];
    int age;
};
 
int save(char * filename, struct person *p);
int load(char * filename);
 
int main(void)
{
    char * filename = "person.dat";
    struct person tom = { "Tom", 21 };
 
    save(filename, &tom);
    load(filename);
     
    return 0;
}
 
// запись структуры в файл
int save(char * filename, struct person *p)
{
    FILE * fp;
    char *c;
    int size = sizeof(struct person); // количество записываемых байтов
 
    if ((fp = fopen(filename, "wb")) == NULL)
    {
        perror("Error occured while opening file");
        return 1;
    }
    // устанавливаем указатель на начало структуры
    c = (char *)p;
    // посимвольно записываем в файл структуру
    for (int i = 0; i < size; i++)
    {
        putc(*c++, fp);
    }
    fclose(fp);
    return 0;
}
 
// загрузка из файла структуры
int load(char * filename)
{
    FILE * fp;
    char *c;
    int i; // для считывания одного символа
    // количество считываемых байтов
    int size = sizeof(struct person);
    // выделяем память для считываемой структуры
    struct person * ptr = (struct person *) malloc(size);
 
    if ((fp = fopen(filename, "rb")) == NULL)
    {
        perror("Error occured while opening file");
        return 1;
    }
 
    // устанавливаем указатель на начало блока выделенной памяти
    c = (char *)ptr;
    // считываем посимвольно из файла
    while ((i = getc(fp))!=EOF)
    {
        *c = i;
        c++;
    }
 
    fclose(fp);
    // вывод на консоль загруженной структуры
    printf("%-20s %5d \n", ptr->name, ptr->age);
    free(ptr);
    return 0;
}

При записи ми отримуємо покажчик на структуру, яка містить початкову адресу блоку пам'яті, за яким розташовується структура. Для структури виділяється 16 + 4 = 20 байт.

Функція putc записує окремий символ в файл, проте нам треба записати структуру. Для цього ми створюємо покажчик на символ (який по суті являє один байт) і встановлюємо цей покажчик на початок блоку пам'яті, виділеного для структури.

c = (char *)p;

Тобто в даному випадку ми отримуємо адресу в пам'яті першого байта з блоку пам'яті, яка виділена для структури. І потім ми можемо пройтися по всьому цьому блоку і отримати окремі байти і занести їх в файл:

for (int i = 0; i < size; i++)
{
    putc(*c++, fp);
}

І в даному випадку нам не важливо, які поля має структура, якою вона має розмір. Ми працюємо з нею як з набором байт і заносимо ці байти в файл. Після занесення кожного окремого байта в файл покажчик c в блоці пам'яті переміщається на один байт вперед.

При читанні файлу використовується схожий принцип тільки у зворотний бік.

По-перше, для зчитування структури з файлу ми виділяємо блок динамічної пам'яті для зберігання прочитаних даних:

struct person * ptr = (struct person *) malloc(size);

Після цього покажчик ptr буде вказувати на першу адресу блоку з 20 байт.

Потім так як при прочитанні ми отримуємо символи, встановлюємо покажчик на символ на початок виділеного блоку і в циклі зчитуємо дані з файлу в цей блок:

c = (char *)ptr;
// считываем посимвольно из файла
while ((i = getc(fp))!=EOF)
{
    *c = i;
    c++;
}

Тут варто звернути увагу на те, що в даному випадку насправді зчитуємо навіть не символ, а числовий код символу в змінну типу int і тільки потім передаємо значення вказівником c. Це зроблено для коректної обробки закінчення файлу EOF. Це значення може представляти будь-яке негативне число. І якби ми зберегли негативне число (наприклад, вік користувача був би негативним), то воно було б некоректно інтерпретовано при читанні як кінець файлу, і підсумковий результа був би невизначеним. Тому більш правильно зчитувати саме числовий код символу в змінну int, а потім числовий код передавати в char.

Запис і читання масиву структур

Вище наведено приклад по роботі з однією структурою. Але, як правило, при роботі з файлами ми оперуємо не однієї структурою, а якимось набором структур. Тому ускладнити завдання і збережемо і вважаємо з файлу масив структур:

#include <stdio.h>
#include <stdlib.h>
 
struct person
{
    char name[20];
    int age;
};
 
int save(char * filename, struct person *st, int n);
int load(char * filename);
 
int main(void)
{
    char * filename = "people.dat";
    struct person people[] = { "Tom", 23, "Alice", 27, "Bob", 31, "Kate", 29 };
    int n = sizeof(people) / sizeof(people[0]);
 
    save(filename, people, n);
    load(filename);
    return 0;
}
 
// запись в файл массива структур
int save(char * filename, struct person * st, int n)
{
    FILE * fp;
    char *c;
 
    // число записываемых байтов
    int size = n * sizeof(struct person);
     
    if ((fp = fopen(filename, "wb")) == NULL)
    {
        perror("Error occured while opening file");
        return 1;
    }
    // записываем количество структур
    c = (char *)&n;
    for (int i = 0; i<sizeof(int); i++)
    {
        putc(*c++, fp);
    }
 
    // посимвольно записываем в файл все структуры
    c = (char *)st;
    for (int i = 0; i < size; i++)
    {
        putc(*c, fp);
        c++;
    }
    fclose(fp);
    return 0;
}
 
// загрузка из файла массива структур
int load(char * filename)
{
    FILE * fp;
    char *c;
    int m = sizeof(int);
    int n, i;
 
    // выделяем память для количества данных
    int *pti = (int *)malloc(m);
 
    if ((fp = fopen(filename, "r")) == NULL)
    {
        perror("Error occured while opening file");
        return 1;
    }
    // считываем количество структур
    c = (char *)pti;
    while (m>0)
    {
        i = getc(fp);
        if (i == EOF) break;
        *c = i;
        c++;
        m--;
    }
    //получаем число элементов
    n = *pti;
 
    // выделяем память для считанного массива структур
    struct person * ptr = (struct person *) malloc(n * sizeof(struct person));
    c = (char *)ptr;
    // после записи считываем посимвольно из файла
    while ((i= getc(fp))!=EOF)
    {
        *c = i;
        c++;
    }
    // перебор загруженных элементов и вывод на консоль
    printf("\n%d people in the file stored\n\n", n);
 
    for (int k = 0; k<n; k++)
    {
        printf("%-5d %-20s %5d \n", k + 1, (ptr + k)->name, (ptr + k)->age);
    }
 
    free(pti);
    free(ptr);
    fclose(fp);
    return 0;
}

Дане завдання ускладнена тим, що нам треба зберігати масив структур, кількість яких точно може бути невідомо. Один з варіантів рещенія цієї проблеми полягає в збереженні деякої метаінформації про фото на початку файлу. Зокрема, в даному випадку на початку файлу зберігається число записаних структур.

Запис багато в чому аналогічна записи однієї структури. Спочатку встановлюємо покажчик на число n, яке представляє кількість структур, і всі байти цього числа записуємо в файл:

c = (char *)&n;
for (int i = 0; i<sizeof(int); i++)
{
    putc(*c++, fp);
}

Потім так само записуємо все байти з масиву структур.

При читанні нам доведеться файктіческі зчитувати з файлу два значення: кількість структур і їх масив. Тому при читанні два рази виділяється пам'ять. Спочатку для кількості елементів:

int *pti = (int *)malloc(m);

Потім ми зчитуємо перші 4 байта з файлу для отримання числа:

c = (char *)pti;
while (m>0)
{
    i = getc(fp);
    if (i == EOF) break;
    *c = i;
    c++;
    m--;
}
//получаем число элементов
n = *pti;

Потім аналогічні дії проробляємо для масиву структур.

І результатом програми має бути висновок лічених даних:

4 людини, що зберігаються в файлі

1 Tom 23
2 Аліса 27
3 Боб 31
4 Кейт 29

Наш партнер:
beta test mp3 playlist downloader