Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lektsii_OP / T14.doc
Скачиваний:
136
Добавлен:
17.03.2016
Размер:
405.5 Кб
Скачать

Робота з бінарними файлами

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

Задання типу файла, як бінарного, у С-програмах здійснюється додаванням суфікса b до значення режиму відкриття файлу (наприкінці чи перед знаком “+”). Наприклад, створення бінарного файла в режимі читання/запису:

FILE *f = fopen("D:\file.txt", "wb+");

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

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

Прототип функції блокового зчитування fread():

size_t fread

( char* buffer, // покажчик на масив для зчитування даних

size_t elemSize, // розмір одного елемента

size_t numElems, // кількість елементів для зчитування

FILE *f // покажчик файлового потоку

);

Тут size_t означений як беззнаковий цілий тип у системних заголовних файлах. Функція fread() намагається прочитати із файла, який задається покажчиком f, numElems елементів, кожен із яких має розмір elemSize, і помістити зчитаний блок інформації у масив, на який вказує покажчик buffer. Як результат повертається реальна кількість зчитаних елементів (яка може бути менша за numElems у разі завершення файла або помилки зчитування) або 0.

Функція блокового запису fwrite() є аналогічною до функції зчитування fread(). Вона має такий прототип:

size_t fwrite

( char* buffer, // покажчик на масив даних, що записуються у файл

size_t elemSize, // розмір одного елемента

size_t numElems, // кількість елементів для запису

FILE *f // покажчик файлового потоку

);

Дана функція записує в кінець файла, який задається покажчиком f, numElems елементів, кожен із яких має розмір elemSize, вибираючи їх за покажчиком buffer. Як результат повертається кількість реально записаних елементів, яка може бути менше за numElems, якщо при записуванні виникла помилка (приміром, не вистачило вільного простору на диску).

Функції fread() і fwrite() дозволяють читати і записувати у вигляді блоків певного розміру дані будь-якого виду і структури. Наприклад,

FILE *f;

char list[30];

int i, n_read, n_written;

if((f = fopen("fread.out", "wb"))!= NULL)

{ for (i = 0; i<25; i++)

list[i] = (char)('z'- i);

n_written = fwrite(list, sizeof(char), 25, f);

printf("Wrote %d items\n", n_written);

fclose(f);

}

else printf("Problem opening the file \n");

if((f = fopen("fread.out", "rb"))!= NULL )

{ n_read = fread(list, sizeof(char), 25, f);

printf("Number of items read = %d\n", n_read);

printf("Contents of buffer = %.25s\n", list);

fclose(f);

}

else printf("File not found \n");

В даному прикладі масив list виступає в якості буфера для зчитування інформації з бінарного файлу f та записування її у файл f. Спочатку елементи буфера ініціалізуються літерами латинського алфавіту від z до b, а потім записуються у файл за допомогою функції fwrite(). Аналогічним чином за допомогою функції fread() здійснюється зчитування інформації з файлу і в масив list поміщаються 25 символів, що зберігаються у файлі.

Одне з найбільш корисних застосувань функцій fread() і fwrite() - це читання і запис блоків даних типу масивів або структур. Наприклад, для раніше описаної структури TBook, що зберігає інформацію про книги (назва, автор, рік видання), функції збереження відповідної інформації в бінарний файл і її зчитування із бінарного файлу можуть мати наступний вигляд:

void creat_file()

{ f = fopen("file.txt","wb");

if ( f== NULL) printf(“Cannot open file \n”);

else

{ cout<<" Enter number of records:"; cin>>n;

for (int i=0; i<n; i++)

{ scanf("%s", book.name);

scanf("%s", book.author);

scanf("%d", &book.year);

fwrite(&book, sizeof(TBook), 1, f);

}

fclose(f);

}

}

void print_file()

{ f = fopen("file.txt","rb");

if ( f== NULL) printf(“File not found \n”);

else

{ while (!feof(f))

{ fread(&book, sizeof(TBook), 1, f);

printf("%s %s %d\n", book.name, book.author, book.year);

}

fclose(f);

}

}

У даному прикладі функції fwrite() і fread() по суті здійснюють копіювання заданої області пам'яті у файл, а потім назад. Цю їх властивість зручно використовувати при зберіганні «складних» форм даних, коли простий поелементний запис даних у файл стає трудомістким або неможливим.

Для введення-виведення несимвольних компонент, зокрема, чисел, можна використовувати і функції форматованого введення-виведення fscanf() та fprintf().

Наприклад, є файл даних, який містить цілі числа, розділені пробілами. Кількість чисел у файлі невідома. Потрібно знайти середнє арифметичне значення цих чисел.

int s=0, //сума чисел

count=0, //кількість чисел

numb; //поточне число

FILE *f;

if ((f=fopen("file .dat","rb"))!=NULL)

{ while (!feof(f))

{ fscanf(f, "%d", &numb);

s +=numb;

count++;

printf("%d\n", numb);

}

double aver=(double) s/count;

printf("Average=%f \n", aver);

fclose(f);

}

else printf("\n File not found!");

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

Хоча дані у бінарні файли записуються послідовно, у порядку їхнього надходження, оброблятися вони можуть як послідовно, так і за допомогою прямого доступу – коли читання або запис довільного елемента здійснюється безпосередньо за його номером (адресою). Це обумовлено тим, що дані в бінарних файлах умовно розділені на блоки однакового розміру, і положення будь-якого із блоків легко можна обчислити.

Можливості напряму позиціонувати файловий покажчик на потрібний елемент (байт) файлу є практично в усіх розвинених мовах програмування. Зокрема, у Pascal позиціонування всередині файлу забезпечується процедурою Seek, у С/С++ для цього служить функція fseek(). Її прототип:

int fseek(FILE * f, long offset, int start);

Ця функція задає зсув файлового покажчика f на кількість байтів offset відносно точки відліку, яка задається параметром start. Зсув (offset) задається виразом або змінною і може бути від’ємним, тобто можливе переміщення як у прямому, так і в зворотному напрямках. Параметр start може набувати таких значень (визначених у заголовку stdio.h):

SEEK_SET – початок файла;

SEEK_CUR – поточна позиція;

SEEK_END – кінець файла.

Дані константи визначені як цілочисельні значення, причому SEEK_SET відповідає 0, SEEK_CUR - 1, a SEEK_END - 2. Отже, для переходу на певне число байт від початку файла слід встановити початок в SEEK_SET; для переходу від поточної позиції треба використовувати SEEK_CUR, а для переходу від кінця файлу - SEEK_END. Наприклад, функція пошуку заданої структури TBook:

void find(int n)

{ if ((f= fopen("file.dat", "rb")) != NULL)

{ fseek(f, n*sizeof(TBook), SEEK_SET); // пошук структури

fread(&book, sizeof(TBook), 1, f); // зчитування даних в пам’ять

}

else printf("File not found \n");

fclose(f);

}

Можливість переміщувати позицію є корисною для файлів, які складаються з однорідних записів однакового розміру. Приміром, якщо у файлі записано лише дійсні числа типу double, то для того, щоб прочитати i-тe число, слід виконати оператори:

fseek(f, sizeof(double)*(i-1), SEEK_SET); // fseek(f, sizeof(double)*(i-1), 0);

fread(&а, sizeof(double), 1, f);

В такий спосіб можна читати які завгодно записи в будь-якій послідовності. За допомогою переміщування позиції можна редагувати записи у файлі. Нехай, приміром, треба змінити у файлі одне з чисел, помноживши його на 5. Це можна зробити, якщо відкрити файл у режимі читання/запису (наприклад у форматі "r+b"), переміститись у відповідну позицію і виконати оператори

fread(&а, sizeof(double), 1, f);

а *= 5;

fseek(f, -sizeof(double), SEEK_CUR); // fseek(f, -sizeof(double), 1);

fwrite(&а, sizeof(double), 1, f);

Перший з цих операторів читає число з файла у дійсну змінну a типу double, другий – домножує її на 5. Третій оператор повертає позицію на один запис назад, оскільки після виконання fread() позиція просунулась уперед. Останній оператор пише в ту позицію, з якої було прочитано число, нове значення.

Встановити файловий покажчик на початок файлу в С-програмах можна також за допомогою функції rewind(), яка встановлює покажчик поточної позиції у файлі на початок файлу, вказаного як аргумент цієї функції. Її прототип:

void rewind(FILE *f);

Прямий доступ в поєднанні з відсутністю перетворень забезпечує високу швидкість отримання потрібної інформації.

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

long int ftell(FILE *f );

Для бінарних потоків цією функцією повертається значення, що відповідає кількості байт від початку файлу. Тому за допомогою функцій ftell() та fseek() можна, наприклад, визначити сумарний обсяг пам’яті у байтах, який займає файл:

fseek(f, 0, SEEK_END); // перемістити внутрішній покажчик в кінець файла

long size = ftell(f); // повернути поточне положення файлового покажчика

cout << "File size: " << size << " byte\n";

Приклад. На основі сформованого файла цілих чисел створити новий файл, в який переписати числа, що зустрічаються у вихідному файлі тільки один раз.

#include <stdio.h>

#include <conio.h>

#include <iostream.h>

#include <stdlib.h>

using namespace std;

FILE *in,*curr,*out; // покажчики на вхідний, проміжковий та вихідний файли

int i, j, // поточні значення елементів файлів

m, // індикатор співпадаючих елементів

ind_i=0,ind_j=0; // поточні номера елементів файлів

char c; // індикатор кінця введення

char * Input(); // створення файла цілих чисел

void Output(char []); // виведення на екран вмісту файла

void CopyF(char []); // копіювання файлів

void DelDubl(char []); // пошук елементів файлів, що повторюються

//---------------------------- головна функцiя ------------------------------------------------------------

main()

{ clrscr();

char *name_in = Input();

Output(name_in);

CopyF(name_in);

Output("curr.dat");

DelDubl(name_in);

Output("out.dat");

cout<<endl;

system(“pause”);

}

//-----------------------створення файла цілих чисел -----------------------------------------------

char *Input()

{ static char name[20];

cout<<“Enter file name:”; cin>>name;

FILE *f= fopen(name,"wb");

do

{ cout<<“Enter number:"; scanf("%d",&i);

fprintf(f,"%d ", i);

cout<<"Continue?(Y/N)"; cin>>c;

}

while ((c!='n')&&(c!='N'));

fclose(f);

return name;

}

//-------------------------- виведення на екран вмісту файла ---------------------------------------

void Output(char name[])

{ FILE *f=fopen(name,"r");

printf("\nFile %s:", name);

while (! feof(f)) // поки не кінець файла,

{ fscanf(f,"%d ",&i); // зчитати із файла в ОП

printf("%d ",i); // друк значень із ОП

}

fclose(f);

}

//----------------------- копіювання файлів -----------------------------------------------

void CopyF(char name[])

{ FILE *curr=fopen("curr.dat","wb");

FILE *in=fopen(name,"rb");

while (! feof(in))

{ fscanf(in,"%d ",&i);

fprintf(curr,"%d ",i);

}

fclose(curr);

fclose(in);

}

//----------------------- пошук елементів файлів, що повторюються ----------------------------

void DelDubl(char name[])

{ curr= fopen("curr.dat","rb");

in= fopen(name,"rb");

out= fopen("out.dat","wb");

while (!feof(in))

{ fscanf(in,"%d ",&i); // читати елемент із вхідного файла

m=0;

ind_i++;

fseek(curr, 0L, SEEK_SET);

ind_j=0;

while (! feof(curr))

{ fscanf(curr,"%d ",&j); // читати елемент із допоміжного файла

ind_j+=1;

if ((i==j)&&(ind_i!=ind_j)) m++;

}

if (m==0) fprintf(out,"%d ",i);

}

fclose(out);

fclose(curr);

fclose(in);

}

1Послідовний доступ - основна властивість усіх запам'ятовуючих пристроїв з механічним переміщенням носія інформації та (або) зчитуючої (записуючої) "головки"

2Дескриптор файлу (від англ. handle)це його унікальний ідентифікатор, призначений операційною системою при відкритті файла

3 Макрос EOF є значенням, яке повертається тоді, коли функція введення намагається виконати читання після кінця файлу

23

Соседние файлы в папке lektsii_OP