
C _Учебник_МОНУ
.pdfТипи користувача |
379 |
Розглянемо кілька прикладів застосовування операцій, поданих у табл. 11.2:
Set <short, 0, |
200> f; |
|
f << 1 << 5 |
<< |
19 << 119;// f складається з 1, 5, 19, 119 |
Set <short, |
0, |
200> f1; |
f1 << 2 << 45; |
// f1 складається з 2, 45 |
f = f.operator +(f1); |
//або f+=f1; тепер f складається з 1,2,5,19,45,119 |
Set <short, 0, 200> f2;
f2 << 2 << 5 << 19 << 47; // f2 складається з 2, 5, 19, 47 f = f.operator * (f2); // Операція operator* або f *= f1;
// f є перетином множин f та f2, в якому залишилися числа 2, 5 та 19
Наведемо приклади застосовування множин на практиці. Якщо треба обмежити можливість вводити до вікна редактора Edit1 лише числа від 0 до 9, для цього у відгуку події OnKeyPress вікна Edit1 слід записати такі оператори:
Set <char, '0', '9'> Dig;
Dig <<'0'<<'1'<<'2'<<'3'<<'4'<<'5'<<'6'<<'7'<<'8'<<'9'; if(!Dig.Contains(Key)) Key=0;
Відомо, що для компонента StringGrid властивість Options визначена як множина такого вигляду:
enum TGridOption { goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goDrawFocusSelected, goRowSizing, goColSizing, goRowMoving, goColMoving, goEditing, goTabs, goRowSelect, goAlwaysShowEditor, goThumbTracking };
typedef Set <TGridOption, goFixedVertLine, goThumbTracking> TGridOptions;
Тому, для долучення певних опцій компонента StringGrid1 із зазначених значень множини, наприклад для можливості редагування даних у комірках при виконуванні програми і для переміщення від однієї комірки до іншої натисненням клавіші <Tab>, у програмі можна записати оператор
StringGrid1->Options<<goEditing<<goTabs;
а для вилучення однієї з опцій властивості Options, наприклад goTabs – оператор
StringGrid1->Options>>goTabs;
Питання та завдання для самоконтролю
1)В який спосіб можна створювати нові типи даних на базі вже існуючих? Запишіть оголошення типу Str як масиву з 60-ти символів.
2)Дайте означення структури як типу даних.
3)У чому полягає відмінність структури від масиву?
4)Як здійснюється доступ до полів структури?
5)Як визначається обсяг пам‟яті, потрібний для зберігання структури?
6)Чи можуть в одній програмі збігатися імена полів структури і змінних?
7)Чи можуть збігатися в одній програмі імена полів двох структур?
8)Чи дозволяється створювати вкладені структури?
380 |
Розділ 11 |
9) Придумайте оголошення структури, яка описуватиме для деякого електричного приладу такі характеристики: назва приладу, споживана потужність та номінальна напруга.
10)Чи може матриця бути полем структури? Якщо так, наведіть приклад.
11)Чи має значення порядок слідування полів у описі структури?
12)Чи може структура бути типом елемента масиву? Якщо так, наведіть
приклад.
13)Оголосіть структуру з ім‟ям Student, що містить такі поля: прізвище, назва групи, масив з п‟яти екзаменаційних оцінок. Запишіть команди, які нададуть початкові значення усім полям змінної типу Student.
14)Найдіть та виправте помилки припущені в описі структури: structure { int arr[12], char string, int *sum }
15)Дайте означення типу даних “об‟єднання”.
16)Як визначається обсяг пам‟яті, потрібний для зберігання усіх полів певного об‟єднання?
17)Напишіть об‟єднання, в якому може зберігатися чи то покажчик, чи ціле, чи дійсне число.
18)Охарактеризуйте тип перерахування.
19)Яких значень набувають константи перерахування за відсутності їхньої ініціалізації?
20)Якщо перерахування COLOR задано в такий спосіб, то яким є значення його елементів white, red, blue та yellow?
enum COLOR { white, black=100, red, blue, green=300, yellow};
21)Які помилки містять наведені оператори?
enum State { on, off }; enum YesNo { on, off };
22)Правильним чи ні є таке оголошення перерахування? enum YesNo { no = 0, No = 0, yes = 1, Yes = 1 };
23)Створіть оголошення перерахування response зі значеннями “Так”, “Ні” і “Можливо”. Значення “Так” повинно мати порядковий номер 1, “Ні” – 0 та “Можливо” – 2.
24)Дайте означення множини в С++ Builder.
25)Якими є елементи множини f, якщо її визначено у такий спосіб:
Set <int, 100, 300> f;
f << 1 << 105 << 119 << 245 << 419;
а) 1, 105, 119, 245, 419; б) 105, 119, 245;
в) усі цілі числа в межах від 100 до 300; г) пуста множина, оскільки станеться помилка.
Розділ 12
Файли
12.1 Загальні відомості про файли
Файлами є іменовані області пам‟яті на зовнішньому носії, призначені для довготривалого зберігання інформації. Файли мають імена й є організовані в ієрархічну деревовидну структуру з каталогів (тек) і простих файлів.
Для доступу до даних файла з програми в ній слід прописати функцію відкриття цього файла, тим самим встановити зв‟язок поміж ім‟ям файла і певною файловою змінною у програмі.
Файли відрізняються від звичайних масивів тим, що
вони можуть змінювати свій розмір;
звертання до елементів файлів здійснюється не за допомогою операції індексації [], а за допомогою спеціальних системних викликів та функцій;
доступ до елементів файла відбувається з так званої позиції зчитуван- ня-записування, яка автоматично просувається при операціях зчитуваннязаписування, тобто файл є видимим послідовно. Існують, щоправда, функції для довільного змінювання цієї позиції.
Часто використовується така метафора: якщо уявляти собі файли як книжки (лише зчитування) і блокноти (зчитування й записування), які стоять на полиці, то відкриття файла – це вибір книжки чи блокнота за заголовком на його обкладинці й відкриття обкладинки (на першій сторінці). Після відкриття можна читати, дописувати, викреслювати і правити записи, перегортати книжку. Сторінки можна зіставити з блоками файла, а полицю з книжками – з каталогом.
С++ Builder надає засоби опрацювання двох типів файлів: текстових та
бінарних.
Текстові файли призначено для зберігання текстів, тобто сукупності символьних рядків змінної довжини. Кожен рядок завершується керувальною пос-
лідовністю '\n', а розділювачами слів та чисел у рядку є пробіли й символи табуляції. Оскільки вся інформація текстового файла є символьною, програмне опрацювання такого файла полягає в читанні рядків, виокремлюванні з рядка слів і, за потреби, перетворюванні цифрових символьних послідовностей на числа відповідними функціями перетворювання. Створювати, редагувати текстові файли можна не лише в програмі, а й у якому завгодно текстовому редакторі, наприклад Блокноті чи Word.
Бінарні файли зберігають дані в тому самому форматі, в якому вони були оголошені, і їхній вигляд є такий самий, як і в пам‟яті комп‟ютера. І тому відпадає потреба у використанні розділювачів: пробілів, керувальних послідовностей, а отже, обсяг використовуваної пам‟яті порівняно з текстовими файлами з аналогічною інформацією є значно меншим. Окрім того, немає потреби у застосуванні функцій перетворення числових даних. Але кожне опрацювання даних

Файли |
383 |
Наведемо найпростіший приклад завантаження текстового файла і зберігання змін, якщо їх було внесено. Для цього на формі встановимо компоненти
Memo, OpenDialog, SaveDialog та три кнопки Button й матимемо зображення
Текст програми в C++ Builder:
// Кнопка “Завантажити файл”.
void __fastcall TForm1::Button1Click(TObject *Sender) { if(OpenDialog1->Execute())
Memo1->Lines->LoadFromFile(OpenDialog1->FileName);
}
//Кнопка “Зберегти змінення у файлі”.
//Зберігання файла виконується при зміненні тексту в Memo1.
void __fastcall TForm1::Button2Click(TObject *Sender) { if(Memo1->Modified)
if(SaveDialog1->Execute()) Memo1->Lines->SaveToFile(SaveDialog1->FileName);
}
12.2.2 Робота з текстовими файлами у стилі С
У мові С файл розглядається як потік послідовності байтів. Інформація про файл заноситься до змінної типу FILE*. Цей тип оголошує вказівник потоку, який використовується надалі у всіх операціях з цим файлом. Тип FILE означено у бібліотеці <stdio.h>. Тому, якщо в програмі передбачається робота з файлами, цей заголовний файл слід долучити:
#include <stdio.h>
Тепер можна оголосити файлову змінну – вказівник потоку, який в подальшому буде передаватися у функції введення-виведення у якості параметра:
FILE *f;
Функція fopen() для відкривання файла має такий синтаксис:
FILE *fopen(const char *filename, const char *mode);
Перший параметр filename визначає ім‟я файла, який відкривається. Другий параметр mode задає режим відкривання файла (табл. 12.1).
Файли |
385 |
Перевірка кінця файла здійснюється функцією feof(): int feof (FILE *stream);
Розглянемо детальніше використання цих функцій:
char s[50]; Memo1->Clear(); do { fgets(s,50,f);
Memo1->Lines->Add(s); } while (!feof(f));
Даний приклад ілюструє зчитування даних з файла порядкóво за допомогою функції fgets() і виведення рядків до компонента Memo. Перший параметр функції fgets() – це С-рядок, в який читається черговий рядок текстового файла. Зчитування рядка відбувається до появи символу кінця рядка '\n' або ж припиняється, коли прочитано m-1 символ. У нашому прикладі m=50. Отже, якщо файл містить рядок, який має понад 50 символів, то буде прочитано лише перші 49 символів. При цьому поточна позиція файла залишиться в тому самому рядку й за подальшого використання функції fgets() читатиметься залишок рядка. Третій параметр зазначає потік, з якого здійснюється зчитування.
У даному прикладі при зчитуванні всіх даних з файла f використовується функція feof(f), яка перевіряє, чи не прочитано символ кінця файла. Після зчитування цього символу функція feof(f) поверне ненульове значення – і цикл перерветься.
У наведеному прикладі є два недоліки:
1) річ у тім, що прочитаний рядок може завершуватися символом '\n', який є ознакою кінця рядка. Цей символ також відобразиться у вікні Memo й “зіпсує” вигляд вихідних даних. Усунути цей недолік можна за допомогою перевірки командою
if(s[strlen(s)-1]=='\n') s[strlen(s)-1]= '\0';
який прибере з рядка символ '\n';
2) використання функції feof() часто призводить до появи дубліката останнього рядка файла, тому рекомендовано використовувати в якості умови циклу функцію зчитування даних, наприклад fgets(), або контролювати добігання кінця файла, тобто перевіряти, чи прочитано всі записані у файлі символи (нагадаємо, що 1 символ – 1 байт). Наведений приклад зчитування рядків з файла можна тепер записати в такий спосіб:
while(fgets(s,50,f))
{if(s[strlen(s)-1]=='\n') s[strlen(s)-1] = '\0'; Memo1->Lines->Add(s); }
Зчитування форматованих даних можна також здійснювати за допомогою функції fscanf():
int fscanf(FILE *stream, const char *format[, address.]);
Наведемо приклад використання цієї функції:
float r; Memo1->Clear();
while(fscanf(f,"%e",&r)>0) Memo1->Lines->Add(FloatToStr(r));


Файли |
387 |
Записування до текстового файла можна здійснити також за допомогою функції fprintf():
int fprintf(FILE *stream, const char *format [, argument]);
Ця функція є подібна до функції fscanf(), але має ширші можливості побудови рядка форматування, наприклад:
char s[20]; strcpy(s, "Іванов"); int year=1985;
fprintf(F, "ХАРАКТЕРИСТИКА\nспівробітник %s, %i р.н.\n", &s, year);
У результаті виконання цих команд до файла буде записано таке:
ХАРАКТЕРИСТИКА співробітник Іванов, 1985 р.н.
Як бінарні, так і текстові файли дозволяють переміщувати поточну позицію зчитування-записування. Для визначення поточної позиції файла, яка автоматично зміщується на кількість опрацьованих байтів, використовується функ-
ція ftell():
long int ftell(FILE *stream);
A змінити поточну позицію файла можна за допомогою функції fseek(): int fseek(FILE *stream, long offset, int whence);
Ця функція задає зсув на кількість байтів offset щодо точки відліку, яка задається параметром whence. Параметр whence може набувати таких значень:
Константа |
whence |
Точка відліку |
SEEK_SET |
0 |
початок файла |
SEEK_CUR |
1 |
поточна позиція |
SEEK_END |
2 |
кінець файла |
Наприклад, для переміщення поточної позиції на початок файла можна скористатися функцією
fseek(f, 0, SEEK_SET);
або
fseek(f, 0, 0);
За допомогою функцій ftell() та fseek() можна визначити сумарний обсяг пам‟яті у байтах, який займає файл. Для цього є достатньо переміститися на кінець файла:
fseek(f, 0, SEEK_END); int d = ftell(f);
Отже, послідовність роботи з файлом при записуванні даних є така: 1) Відкрити чи то створити файл fname.txt для записування (або для
зчитування й записування) у кінець файла: f=fopen("fname.txt", "at+");
