Файли з даними типу структура

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

Наприклад, якщо структура описана у вигляді:

stuct rab

{

char fio[20];           // фамилия рабочего

int gr;                     // год рождения и зарплата

float zarpl;

} sp;                         //sp — переменная типа структура

то ввести в пам’ять комп’ютера поля змінної sp типу структура можна таким чином:

AnsiString(sp.fio) = InputBox(“”, “Введите фамилию”, “”);

AnsiString р = InputBox(“”, “Введите год рождения”, “”);

sp.gr = StrToInt(p);

р = InputBox(“”, “Введите зарплату”, “”);

sp.zarpl = StrToFloat(p);

Запис змінної sp у файл та читання її з файла здійснюєть­ся відповідно операторами

Write((char*)&sp, sizeof(sp)) та Read((char*)&sp, sizeof(sp));.

Ці операції можна робити, якщо попередньо був відкритий відповідний файл.

Виведення значеннь полів змінної sp після читання фай­ла можна здійснити з використанням будь-якого розглянутого раніш компонента виведення, наприклад:

Edit1->Text=sp.fio+” “+IntToStr(sp.gr)+ ” “+FloatToStrF(sp.zarpl,ffFixed,8,2);

За необхідності розміщення на формі великої кількості кно­пок, що породжують події, доцільно використовувати компонен­ти MainMenu або PopUpMenu, які дозволяють створювати меню команд різного вигляду.

Приклад 16.7. Розробити додаток, що організує роботу з розкла­дом занять і реалізує виконання таких команд:

—   введення назви групи;

—   запис розкладу занять групи у файл;

—   читання розкладу занять з файла;

—   читання розкладу занять на заданий день;

—   виведення днів, коли є заняття із заданого предмета;

—   закінчити роботу додатка.

Для реалізації прикладу будемо використовувати компонент MainMenu, що дозволяє створювати систему головних команд меню і для кожної з них — групу команд підменю.

Спочатку розмістимо на формі компонент MainMenu у міс­ці, яке менш за все використовується, наприклад, у її ліво­му нижньому куті. Потім здійснемо конструювання самого ме­ню. Для цього необхідно двічі клацнути лівою клавішею миш­ки на компоненті, й у лівому верхньому куті форми з’явиться прямокутник, призначений для створення першої головної ко­манди меню. У властивість Caption редактора Object Inspector треба ввести ім’я цієї команди («Расписание»), Потім слід пе­ревести курсор нижче цього прямокутника і знову клацнути один раз клавішею миші. На цьому місці з’явиться новий пря­мокутник, призначений для розміщення підкоманди головної команди. Ім’я підкоманди знову вводимо в Object Inspector. Цей процес повторюється стільки разів, скільки підкоманд передбачається розмістити в даній команді (у даному випад­ку їх шість). Далі курсор слід розмістити .праворуч від першої головної команди і знову клацнути мишею. З’явиться пря­мокутник для розміщення нової головної команди, для якої треба ввести ім’я і вищеописаним способом організувати роз­міщення її підкоманд (це — «Дни недели»). Кожну з підко­манд можна перейменувати, зробивши її активною, тобто клац­нути клавішею миші. Виділена команда виводиться яскраво-синім кольором. Якщо в системі підкоманд необхідно вивести розділювальну рису, слід замість введення імені натиснути кла­вішу «—».

Для виведення результатів розмістимо на формі компонент Меmо. Після закінчення формування зовнішнього вигляду ко­манд меню рекомендується записати створений проект на диск, як це робилося в попередніх прикладах.

Вигляд форми і текст програмного коду даного додатка наведені нижче.

#include< vcl.h>

#pragma hdrstop

#include< fstream.h>

#include< Dialogs.hpp>

#include< string.h>

#include “Unit R5.h”

#pragma package (smart_init)

#pragma resource “*.dfm”

TForm1 “Form1;



Наведемо пояснення до розробки програмного коду подій для кожної підкоманди. Спочатку слід увести імена заголовних фай­лів, для даного прикладу це — fstream.h, string.h і Dialogs.hpp.

Далі необхідно описати глобальні змінні до першого оброб­лювача додатка. У нашому випадку це змінна fname, що збері­гав повне ім’я файла, в якому запишеться розклад, та змінна типу структура sp.

Структура має вигляд:

struct rasp

{ char den [15];

char str[3][80]; } sp;,

де char den[15]; — поле, яке зберігає назви дня тижня;

str[3][80] — масив з трьох рядків, необхідний для запису в кожному рядку назви предмета, номера пари, на якій він чи­тається, та номера аудиторії.

Варто зауважити, що більшість компонентів і функцій С++ Builder передбачає запис у їхніх полях введення рядкових да­них типу AnsiString. Синонімом цього службового слова є String (із заголовної літери). І, здавалося б, рядкові поля структури слід описувати як елементи цього типу. Однак оскільки довжи­на таких даних залежить від фактичної довжини рядка, то при записі їх у двійковий файл практично не можливо домогтися, щоб усі компоненти файла мали однакову довжину, через що си­стема дає збій.

Тому рядкові поля структури слід оголошувати як дані ти­пу char заданої довжини, а при їх використанні в компонентах і функціях головної бібліотеки VCL системи С++ Builder роби­ти необхідні перетворення типів.

В прикладі, що розглядається, для введення значення поля структури sp.den, як і інших полів, використано два оператори:

Sting dn = InputВох(” “.”Введите” + IntToStr(I+l) + “день”, ” “);

strcpy(sp.den, dn.c_str());

Перший з них здійснює присвоювання даного, що вводить­ся, тобто назву дня тижня змінній dn типу AnsiSting. У друго­му операторі це дане передавалося в поле sp.den за допомогою функції копіювання strcpy() при одночасному його перетворенні до типу char за допомогою функції dn.c_str();.

Визначивши глобальні змінні, необхідно приступити до на­писання коду оброблювача першої команди меню. Вона нази­вається «Имя группы» і призначена для зазначення шифру групи. Шифр потрібний, тому, що додаток передбачає створен­ня необмеженої кількості розкладів для груп. Цей оброблювач складається з двох операторів, у першому з який змінній sg присвоюється фактичне ім’я групи, а в другому це ім’я додається до глобальної змінної name, завдяки чому в змінній fname формується повне ім’я файла. Наприклад, якщо при виконанні функції InputBox() ввести шифр групи «ФБЕ», то змінна fname буде зберігати значення:

“D:\\Users\\Rasp\yi>BE”;.

Другий оброблювач забезпечує реалізацію наступної коман­ди меню — «Записать». Він починається функцією MessageBox(), що попереджав користувача про випадкове виконання цієї ко­манди, яке приведе до затирання раніше записаного розкладу. Ця функція виводить на екран вікно з написом: «Хотите переписать расписание?» і дві кнопки «ОК» і «Cancel». Натисканням однієї з них користувач підтверджує чи скасовує своє рішення.

Далі в оброблювачі відкривається файл для запису і перед­бачається введення полів структури, як це вказувалося раніше на прикладі введення дня тижня. Для цього організовано два цикли for, один із яких (зовнішній) забезпечує введення даних для кожного з днів тижня, а другий (внутрішній) служить для забезпечення введення назви предмета, номера пари й аудиторії кожного дня. Передбачається, що кількість пар не перевищує трьох. Після введення даних одного дня занять вони запису­ються в один рядок масиву str[k], який є полем раніше описа­ної структури. Цей запис здійснюється за допомогою функції sprintf() і потім записується у файл оператором ft.write(). Він є останнім оператором зовнішнього циклу, що повторюється шість разів, забезпечуючи введення і запис розкладу на кожен день тижня.

Наступний оброблювач команди «Прочитать», призначений для читання всього розкладу в поле введення компоненти Memo1, записано наступним у програмному коді додатка під назвою «Чтение расписания». У ньому спочатку відкриваєть­ся створений файл для читання, очищається поле компонента Memo1, потім виводиться заголовок: «Расписание занятий» і включаються такі ж цикли, як і при записі файла.

У зовнішньому циклі спочатку читається черговий компо­нент файла за допомогою оператора fin.read(), у Memo1 запи­сується назва чергового дня тижня і включається внутрішній цикл, у якому виводяться в Memo1 раніше записані рядки str[k], що містять інформацію про назву предмета, номер пари і аудиторії.

У наступних оброблювачах наводяться програмні коди чи­тання розкладу за заданим днем тижня. Спочатку відкрива­ється файл для читання, потім функцією seekg(n*sizeof(rasp)) встановлюється покажчик читання на потрібний компонент файла, здійснюється читання цього компонента і виведення ін­формації у вікно Memol. Тут n — це номер компонента, що починається з нуля і відповідає в даній програмі номеру чергового дня тижня, sizeof(rasp) — кількість байтів, займаних змінною rasp. При виконанні оператора відкриття файла цьому покажчику автоматично присвоюється початкове нульове зна­чення. В оброблювачі виведення розкладу на понеділок (перший день тижня) функція seekg(n*sizeof(rasp)) не використову­ється, тому що n = 0. Вона призначена для організації прямого доступу до компонентів файла, на відміну від раніше викорис­товуваного послідовного доступу. Прямий доступ можна орга­нізувати не тільки при читанні, але і при записі файлів, під час їх коригування. Для цього треба використовувати функцію seekp() (див. розділ 10).

Для визначення позиції, в якій знаходиться покажчик пото­ку в даний момент, використовуються функції:

in.tellg() та in.tellp() — відповідно при читанні та записі файла,

де іn — це ім’я потоку, створеного операторами ifstream() або ofstream().

У програмному коді даного прикладу наведено оброблювачі виведення розкладу тільки на перші три дні тижня, інші опущені заради економії місця, оскільки вони відрізняються один від одного тільки значенням параметра n у функції seekg().

Оброблювач команди «Предмет» служить для забезпечення виведення розкладу, коли є заняття з даного предмета. У ньо­му за допомогою функції InputBox() вводиться назва предме­та, яка привласнюється змінній st. При цьому можна вводити предмет як без указівки виду занять, що показано в додатку, так і з указівкою, наприклад, «Информатика пз.». Потім ви­значається довжина введеного повідомлення, відкривається файл для читання й організується зовнішній цикл за допомо­гою оператора while(), що забезпечує послідовне читання ком­понентів файла, доки функція eof() не прийме значення true при досягненні маркера кінця файла. При читанні чергового значення змінної sp у внутрішньому циклі for відбувається по­рівняння поля sp.str[k] зі значенням змінної st, у якій запи­сана назва предмета і, якщо вони збігаються, у поле Memo1 буде виведено день, предмет, пару та аудиторію, тобто відпо­відні поля структури str[k]. Приклад виконання цього оброб­лювача ілюструє наведена раніше третя форма.

Попередні приклади використовували в основному методи (функції) класів мови С++: ofstream, ifstream і fstream. Однак середовище С++ Builder має свій клас, в якому визначені влас­тивості і методи роботи з файлами даних. Цей клас входить до складу бібліотеки візуальних компонентів VCL і має ім’я TFileStream. Він частково інкапсулює вище зазначені потоко­ві класи С++, тому є можливість використовувати їхні методи й у даному випадку, тобто обидва способи організації файло­вого введення-виведення взаємозамінними. Методи класу VCL роблять програмний код більш чітким і лаконічним [2].

Для відкриття файла даних треба записати оператор:

TFileStream * fp=new TFileStream (“іф” режим);,

де fp — ім’я потоку, що створюється;

іф — повне ім’я файла (з указівкою шляху);

режим — режим відкриття файла.

Тут можна використовувати такі основні режими:

  • fmCreate — створити файл і відкрити його для запису;
  • fmOpenWrite — відкрити файл тільки для запису з пов­ною заміною поточного його змісту;
  • fmOpenRead — відрити файл тільки для читання;
  • fmOpenReadWrite — відкрити файл для читання і запи­су при модифікації його поточного змісту.

Для класу TFileStream визначені властивості і методи:

  • fp->Position = n; — служить як для визначення поточно­го значення покажчика позиціювання файла, так і для завдан­ня цього значення;
  • fp->Size — визначає поточний загальний розмір даних файла;
  • fp->Read(& р, sizeof(p)); — служить для читання змін­ної р будь-якого типу з файла;
  • fp->Write(*p, sizeof(p)); — записує у файл змінну р.

Наприклад, відкрити файл в режимі читання можна так:

TFileStream*p = new TFileStream(nmyfile.dat”, fmOpenRead) .

Розглянемо особливості використання методів, що наведено вище, на конкретному прикладі.

Приклад 16.8. Записати у файл такі дані:

—   прізвище та ініціали працівників підприємства;

—   рік народження;

—   стать;

—   посадовий оклад.

При читанні створеного файла забезпечити виконання таких дій:

—   вивести зміст усього файла на екран;

—  вивести прізвища чоловіків або жінок заданого року народження;

—   вивести дані заданого працівника.

Для розв’язання прикладу на формі створимо меню, яке складається з двох основних команд: «Файл» та «Вывод». Перша команда складається з підкоманд, що мають назви: «За­писать», «Прочитать» і «Выход», а друга команда містить під­команди: «Мужчин», «Женщин» та «Работника». Для виве­дення інформації розташуємо на формі таблицю StringGrid, яка дозволяє наочно представляти виведені дані і, за необхід­ності, корегувати їх.

Форми і програмний код мають вигляд:




С++: Розв’язання задач з файлами

Для того, щоб не писати щоразу довге і не дуже зручне сло­во StringGrid, задамо в його властивості Name ім’я «Тb». Крім того, встановимо інші властивості компонента, а саме: кількість стовпців, що буде дорівнювати 5, та кількість рядків, яка буде мати значення 10.

У програмному коді підключимо ті ж заголовні файли, що й у попередньому прикладі, потім опишемо глобальні дані: конс­танту name — для збереження повного імені файла, змінну n — для задания кількості працівників і структуру вигляду:

struct trab {

char fio[20];                         // фамилия работника

char pol[5];                           // пол

int gr;                                   // год рождения

float zrp;                              //зарплата

     } rab;,

де rab — змінна типу структура trab.

Опишемо функцію користувача Del(), що служить для очи­щення комірок таблиці шляхом запису порожніх рядків.

Перший оброблювач має ім’я FormCreate й ініціюється при створенні форми, тобто перед запуском програми на виконання. Він активізується при подвійному клацанні на будь-якому міс­ці форми і служить для заповнення заголовка таблиці. Крім того, у ньому визначається кількість працівників, тобто значен­ня глобальної змінної n.

Потім після запуску додатка на виконання заповнимо таб­лицю — запишемо у відповідних комірках необхідну інфор­мацію. Вигляд заповненої таблиці наведено на першій формі прикладу 16.8.

Щоб зберегти значення цієї таблиці, треба записати їх спо­чатку у відповідні поля структури (поля змінної rab), а потім у файл. Ці операції виконуються в оброблювачі з ім’ям N2Click, у якому відкривається файл з ім’ям nаmе для запису.

Комірки таблиці зберігають інформацію у вигляді рядків типу AnsiString, тому при передачі інформації в поля струк­турної змінної rab виконується перетворення даних до типів, що оголошені при описі цих полів.

У кожному рядку таблиці Тb записані всі дані полів струк­турної змінної rab про одного працівника. Після їхньої пере­дачі здійснюється запис цієї змінної у файл за допомогою опе­ратора Write(). Цикл, у якому виконуються ці операції, повто­рюється п разів (за числом працівників), у результаті дані всіх працівників будуть записані у файл.

При виконанні команди меню «Прочитать» відкривається файл для читання, клітки таблиці очищаються від раніше за­писаної інформації за допомогою функції Del(), покажчик позиціонування файла встановлюється в початкове нульове поло­ження, і у циклі for, що повторюється n разів, спочатку з фай­ла читається черговий запис, тобто значення полів змінної rab за допомогою оператора Read(), а потім вони передаються у відповідні клітки таблиці.

При виконанні команд меню «Мужчин» чи «Женщин», після відкриття файла для читання, на екрані з’являється вік­но функції InputBox(), у якому запитується рік народження. Після його введення відбувається очищення таблиці і читання файла в циклі for. При цьому в операторі if здійснюється пере­вірка: якщо значення статі дорівнює заданому і рік народжен­ня збігається з введеним, то в таблицю виводяться дані відпо­відного працівника. Це ілюструє друга форма додатка.

При виконанні команди «Работника» використовуються та­кі ж операції, як і в попередніх оброблювачах, за винятком того, що запитується прізвище працівника і при читанні файла здійснюється його порівняння з полем rab.fio. Якщо вони збі­гаються, то в таблицю виводяться всі дані цього працівника.

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

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *