Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C _Учебник_МОНУ

.pdf
Скачиваний:
206
Добавлен:
12.05.2015
Размер:
11.12 Mб
Скачать

Файли

419

Приклад 12.14 Створити бінарний файл, що містить інформацію про товари: найменування товару, ціна і кількість. Відібрати товари, кількість яких є менше за 30 одиниць. Відредагувати один із записів. Вилучити один із записів. Номер запису для редагування й вилучення ввести з клавіатури.

Розв‟язок. Окрім зазначених алгоритмів опрацювання бінарного файла у цій програмі наведено засоби редагування і вилучення певного запису з файла. Номер такого запису вводиться за допомогою вікна введення InputBox(), синтаксис та приклад роботи якого розглядались у прикладі 6.7. Вікно введення, створене у цій програмі командою

AnsiString snom=InputBox("Який рядок бажаєте вилучити?", "Введіть номер рядка", "1");

матиме вигляд:

Вікно форми проекту з результатами роботи програми:

Текст програми: int n, flag;

//При дописуванні даних у кінець файла flag становитиме 0,

//а при редагуванні даних flag буде мати значення 1;

//n визначатиме номер запису у файлі, причому номер рахуватиметься за кількістю

//рядків, відбитих у компоненті StringGrid, хоча у файлі відлік розпочинається з 0

FILE *f;

char s[]="товар.dat"; // Ім‟я файла struct tovar

{ char N[20];

float price; int kol; };

tovar t;

 

420

Розділ 12

//Кнопка “Дописати до файла”, яка може змінювати свій надпис

//на “Зберегти зміни”.

void __fastcall TForm1::Button1Click(TObject *Sender)

{ if(flag==0)

// У режимі “Дописати до файла”

{if((f=fopen(s,"ab+"))==NULL)

// файл відкривається для дописування

{ ShowMessage("Файла немає!"); return;} // даних у кінець файла strcpy(t.N,Edit1->Text.c_str( )); t.price=StrToFloat(Edit2->Text); t.kol=StrToInt(Edit3->Text);

fwrite(&t,sizeof(tovar),1,f); // Записування нових даних fclose(f);

}

else // У режимі “Зберегти зміни” {if((f=fopen(s,"rb+"))==NULL) // після відкриття позицію розташовано

{ ShowMessage("Файла немає!"); return;} // на початку файла strcpy(t.N,Edit1->Text.c_str( )); t.price=StrToFloat(Edit2->Text); t.kol=StrToInt(Edit3->Text); fseek(f,(n-1)*sizeof(tovar),0);// Переміщення на запис з номером (n-1) fwrite(&t,sizeof(tovar),1,f);

Button1->Caption="Дописати до файла";

fseek(f,0,0); // Переміщення на початок файла int i=2;

while(fread(&t,sizeof(tovar),1,f))

{SG1->RowCount=i; SG1->Cells[0][i-1]=AnsiString(t.N); SG1->Cells[1][i-1]=FormatFloat("0.00",t.price); SG1->Cells[2][i-1]=IntToStr(t.kol);

i++;

}

fclose(f);

flag=0; // Вимкнення режиму “Зберегти зміни”

}

Edit1->Clear(); Edit2->Clear(); Edit3->Clear(); Edit1->SetFocus();

}

// Кнопка “Переглянути файл”

void __fastcall TForm1::Button2Click(TObject *Sender)

{if((f=fopen(s,"rb"))==NULL)

{ShowMessage("Файла немає!"); return; } SG1->Cells[0][0]="Найменування"; SG1->Cells[1][0]="Кільк."; SG1->Cells[2][0]="Ціна";

int i=2; while(fread(&t,sizeof(tovar),1,f))

{SG1->RowCount=i; SG1->Cells[0][i-1]=AnsiString(t.N); SG1->Cells[1][i-1]=FormatFloat("0.00",t.price); SG1->Cells[2][i-1]=IntToStr(t.kol);

Файли

421

i++;

}

fclose(f);

}

// Кнопка “Товари, кількість яких є менше за 30”

void __fastcall TForm1::Button3Click(TObject *Sender)

{if((f=fopen(s,"rb+"))==NULL)

{ShowMessage("Файла немає!"); return; } Memo1->Lines->Clear( ); while(fread(&t,sizeof(tovar),1,f))

if(t.kol<30) Memo1->Lines->Add(AnsiString(t.N)+" "+

FormatFloat("0.00",t.price)+" "+IntToStr(t.kol)); fclose(f);

}

// Кнопка “Видалити запис”

void __fastcall TForm1::Button4Click(TObject *Sender)

{if ((f=fopen(s,"rb"))==NULL)

{ShowMessage("Файла немає!"); return; }

FILE* tmp; // Оголошення вказівника на тимчасовий файл if((tmp=fopen("tmp.dat","wb+"))==NULL) //і створення його.

{ ShowMessage("Файла немає!"); return; }

AnsiString snom=InputBox("Який рядок бажаєте вилучити?", "Введіть номер рядка", "1");

if(snom=="") { ShowMessage("Ви не ввели дані"); return; } int kk, k=StrToInt(snom); while((fread(&t,sizeof(tovar),1,f)))

{kk=ftell(f)/sizeof(tovar); // Функція ftell(f) визначає кількість

// прочитаних байтів, а в змінній kk обчислюється поточний номер запису

if(k==kk) continue; // Пропуск зазначеного запису fwrite(&t,sizeof(tovar),1,tmp);

}

fclose(f);

fseek(tmp,0,0); // Перехід на початок тимчасового файла f=fopen(s, "wb+"); // і нове створення файла для зчитування і записування while((fread(&t,sizeof(tovar),1,tmp))) fwrite(&t,sizeof(tovar),1,f);

fclose(tmp); fclose(f);

remove("tmp.dat");

// Видалення допоміжного файла

Button2Click(NULL);

// Переглядання даних після видалення

}

// Кнопка “ Редагувати запис”

void __fastcall TForm1::Button5Click(TObject *Sender) { if((f=fopen(s,"rb+"))==NULL)

{ ShowMessage("Файла немає!"); return; }

AnsiString snom=InputBox("Який рядок бажаєте коригувати?", "Введіть номер рядка.\nНові значення вводитимуться \n

з відповідних компонентів Edit", "");

if(snom=="") {ShowMessage("Ви не ввели дані"); return;} n=StrToInt(snom);

422

Розділ 12

fseek(f,(n-1)*sizeof(tovar),0);

// Перехід на (n-1)-й запис,

fread(&t,sizeof(tovar),1,f);

// зчитування цього запису

Edit1->Text=AnsiString(t.N);

// і виведення його

Edit2->Text=FormatFloat("0.00",t.price);//до відповідних

Edit3->Text=IntToStr(t.kol);

// компонентів для редагування.

fclose(f);

 

 

flag=1;

// Ввімкнення режиму “Зберегти зміни”

Button1->Caption="Зберегти зміни";// Змінення надпису на кнопці Button2Click(NULL); // Переглядання даних після редагування

}

12.3.2 Робота з бінарними файловими потоками у стилі С++

Для роботи з бінарним форматом файла використовується прапорець ios::binary. Ці файли відкриваються здебільшого в режимі зчитуваннязаписування. Для цього зручними є об‟єкти класу fstream. Дані до бінарних файлів записуються за допомогою методу write() класу ofstream, а зчитуються – за допомогою методу read() класу ifstream.

Наприклад:

fout.write((char)*&A, sizeof(A)); fin.read((char)*&A, sizeof(A));

Перший параметр у цих функцій – адреса змінної А. Ці функції працюють з байтами (типу char). Для них немає жодного значення, в який спосіб організовано й що собою являють дані, – вони просто переносять їх побайтово з буфера до файла та навпаки. Другий параметр – довжина змінної А у байтах. Тому перший оператор обчислює значення виразу sizeof(A) й копіює до файла, пов‟язаного з об‟єктом fout, обчислену кількість байтів, розпочинаючи з вказаної адреси. Другий оператор копіює кількість байтів sizeof(A) з файла fin до змінної А.

У бінарному файлі також існує можливість довільного доступу до файла за допомогою методів seekp() та seekg().

При повторному відкритті файла з використанням тієї ж самої файлової змінної потік слід очищувати за допомогою методу clear(), наприклад: f.clear().

Приклад 12.15 Створити файл з інформацією про успішність студентів: прізвище, ім‟я, оцінки з інформатики, математики та фізики. Вивести на екран середній бал кожного студента. Виявити, чи є серед студентів збіг прізвищ (однофамільці).

Файли

423

Вікно форми проекту з результатами роботи програми:

Текст програми:

#include <fstream.h> fstream f;

char *filename=new char[30]; struct student

{ char name[30], surname[20]; int inf, mat, fiz; };

student A;

void __fastcall TForm1::FormCreate(TObject *Sender)

{ AnsiString fn=InputBox("Відкриття файла", "Введіть ім'я файла","stud.dat");

if(fn=="") { ShowMessage("Ви не ввели дані"); return; } strcpy(filename,fn.c_str());

// Створення чи відкриття файла f.open(filename,ios::out|ios::app|ios::binary); f.clear(); // Очищення потоку

if(!f) { ShowMessage("Помилка відкриття файла"); return; } f.close();

}

// Кнопка “Записати дані до файла”

void __fastcall TForm1::Button1Click(TObject *Sender)

{f.open(filename,ios::out|ios::app|ios::binary);

f.clear();

if(!f) { ShowMessage("Помилка відкриття файла "); return; } f.seekp(0,ios::end); // Позиціонування на кінець файла strcpy(A.name, Edit1->Text.c_str());

strcpy(A.surname, Edit2->Text.c_str()); A.inf=StrToInt(Edit3->Text); A.mat=StrToInt(Edit4->Text); A.fiz=StrToInt(Edit5->Text);

f.write((char*)&A, sizeof(A)); // Записування даних до файла f.close();

}

424

Розділ 12

// Кнопка “Переглянути файл”

void __fastcall TForm1::Button2Click(TObject *Sender)

{Memo1->Clear(); f.open(filename,ios::out|ios::in|ios::binary); f.clear();

if(!f)

{ShowMessage("Помилка відкриття файла "); return; }

while(f.read((char*)&A, sizeof(A))) // Зчитування з файла Memo1->Lines->Add(AnsiString(A.name)+" "+

AnsiString(A.surname)+" "+ IntToStr(A.inf)+" "+ IntToStr(A.mat)+" "+IntToStr(A.fiz));

f.close();

}

// Кнопка “Середній бал кожного студента”

void __fastcall TForm1::Button3Click(TObject *Sender)

{f.open(filename,ios::out|ios::in|ios::binary);

f.clear(); Memo2->Clear();

if(!f) { ShowMessage("Помилка відкриття файла"); return; } double sr;

while(f.read((char*)&A, sizeof(A)))

{ sr=(A.inf + A.mat + A.fiz)/3.; // Обчислення середнього балу Memo2->Lines->Add(AnsiString(A.name) + " " +

AnsiString(A.surname)+" "+FormatFloat("0.0",sr));

}

f.close();

}

// Кнопка “Віднайти однофамільців”

void __fastcall TForm1::Button4Click(TObject *Sender)

{f.open(filename,ios::out|ios::in|ios::binary);

f.clear();

if(!f) { ShowMessage("Помилка відкриття файла"); return; } student A1; long int k=0;

while(f.read((char*)&A, sizeof(A)))

{while(f.read((char*)&A1, sizeof(A1))) if(strcmp(A.name,A1.name)==0)

{ShowMessage("Віднайдено збіг прізвища - " +

AnsiString(A.name));

return;

}

f.clear();

k++;

// Позиціонування на структуру файла з номером k f.seekg(k*sizeof(A),ios::beg);

}

ShowMessage("Збігу прізвищ немає"); f.close();

}

Файли

425

12.3.3 Опрацювання бінарних файлів за допомогою дескрипторів

Для опрацювання бінарних файлів через дескриптори застосовуються функції, які розглянуто в п. 12.2.6.

Нагадаємо порядок дій для записування даних у файл на такому прикладі:

int f; //Оголошення дескриптора f=FileOpen("a.dat",fmOpenWrite);// Відкриття файла для записування

int kb=FileSeek(f, 0, 2);

// Встановлення позиції на кінець файла

 

//і одночасне визначення розміру файла

int k=25;

 

//Записування до файла значення k

цілого типу розміром sizeof(int)

FileWrite(f, &k, sizeof(int));

FileClose(f);

// Закриття файла

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

f=FileOpen("a.dat", fmOpenRead); FileRead(f, (int*)&k, sizeof(int)); FileClose(f);

Відкриття файла для зчитування-записування здійснюватиметься наступною послідовністю команд:

f=FileOpen("a.dat",fmOpenReadWrite); if(f != -1)

/* Далі можна опрацьовувати файл: читати чи записувати данні, встановивши потрібну позицію за допомогою функції FileSeek(). */

Ще раз зауважимо, що за допомогою дескрипторів можна створювати і опрацьовувати файли з даними будь-якого типу.

Розглянемо приклад роботи з бінарним файлом через дескриптор.

Приклад 12.16 Створити файл з інформацією про авіарейси: номер рейсу, маршрут руху, час відправлення (за шаблоном ЧЧ:ММ), час прибуття в пункт призначення (за шаблоном ЧЧ:ММ), при чому з клавіатури мають вводитися: номер рейсу, маршрут руху, час відправлення і тривалість польоту, а час прибуття в пункт призначення слід обчислити. Передбачити можливість вилучення даних про нічні рейси (з 00:00 по 5:00). Відсортувати данні за часом відправлення літаків.

Розв‟язок. У програмі за вибором користувача передбачено можливість виведення даних у компонент Memo1 чи то у компонент StringGrid1, який у програмі перейменовано для зручності на SG1. Тут спеціально наведено засоби виведення даних в обидва компоненти, хоча вочевидь, що в цьому прикладі зовні краще сприймається інформація у StringGrid.

426

Розділ 12

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

Форма після сортування авіарейсів за часом відправлення літаків:

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

Файли

427

Окрім зазначених алгоритмів опрацювання бінарного файла через дескриптор, у програмі наведено засоби вилучення всіх даних файла, тобто обнуління файла. Перед вилученням даних у програмі передбачено виведення діалогового повідомлення MessageDlg(). На відміну від повідомлення ShowMessage(), яке виводить лише вікно з відповідним текстом повідомлення та однією командною кнопкою з надписом ОК, при натисканні на яку вікно повідомлення зникає, діалогове повідомлення MessageDlg() може моделювати кількість кнопок із зазначеними надписами і передбачає можливість програмного реагування залежно від того, яку саме кнопку було натиснуто.

Синтаксис функції MessageDlg()є такий:

int MessageDlg( const AnsiString Msg, TMsgDlgType DlgType, TMsgDlgButtons Buttons, int HelpCtx);

де Msg – текст повідомлення;

DlgType задає характер повідомлення, а саме вигляд піктограми ліворуч від тексту повідомлення. Існують такі можливі значення цього параметра:

mtWarning – зауваження , mtError – помилка , mtInformation – інформа-

ційне повідомлення , mtConfirmation – запит підтвердження , mtCustom – без рисунка;

Buttons задає кількість і вигляд кнопок. Можливі значення цього парамет-

ра: mbYesNoCancel, mbYesNoAllCancel, mbOKCancel, mbAbortRetryIgnore, mbAbortIgnore. Тобто кожна кнопка має відповідне константне значення. Після натискання певної кнопки відповідне константне значення повертається функцією як результат виконання;

HelpCtx визначає екран контекстної довідки, що буде з‟являтися за натискання клавіші <F1>. Якщо така довідка не планується, слід задати значення 0.

Значення параметра Buttons

Відповідне значення, яке повертається

mbOK

mrOk

mbCancel

mrCancel

mbYes

mrYes

mbNo

mrNo

mbAbort

mrAbort

mbRetry

mrRetry

mbIgnore

mrIgnore

mbAll

mrAll

mbNoToAll

mrNoToAll

mbYesToAll

mrYesToAll

Діалогове повідомлення, створене у програмі командою

int btn = MessageDlg("Ви дійсно хочете вилучити всю інформацію з файла?", mtConfirmation, mbOKCancel, 0);

матиме вигляд:

428

Розділ 12

Текст програми

// Глобальні оголошення struct avio_struct

{char num[5], from[20], to[20], all_time[6]; TDateTime start_time, last_time;

AnsiString info( )

{ return AnsiString(num) + " " + (AnsiString)from + " " +

(AnsiString)to + " " + start_time.TimeString( ) + " " + last_time.TimeString( )+" "+(AnsiString)all_time;

} };

 

int size = sizeof(avio_struct);

// Розмір структури

avio_struct w;

// Змінна типу структури

int f;

// Дескриптор

char s[]="a.dat";

// Ім‟я файла

__fastcall TForm1::TForm1(TComponent* Owner)

: TForm(Owner)

// Конструктор форми

{ if (!FileExists(s))

// Якщо такого файла не існувало,

f=FileCreate(s);

// він створюється

FileClose(f);

 

SG1->Cells[0][0]="Номер рейсу";

 

SG1->Cells[1][0]="Пункт відправлення";

SG1->Cells[2][0]="Пункт прибуття"; SG1->Cells[3][0]="Час відправлення"; SG1->Cells[4][0]="Час прибуття"; SG1->Cells[5][0]="Тривалість";

}

// Кнопка “Записати до файла”

void __fastcall TForm1::Button1Click(TObject *Sender)

{AnsiString s1,s2; strcpy(w.num,Edit1->Text.c_str( )); strcpy(w.from,Edit2->Text.c_str( )); strcpy(w.to,Edit3->Text.c_str( )); w.start_time=Edit4->Text; strcpy(w.all_time,Edit5->Text.c_str( )); s1=(AnsiString)w.all_time;

int n=StrToInt(s1.SubString(1,s1.Pos(':')-1)); if (n>=24) n=n%24;

//Створення шаблону ЧЧ:ММ. s2=IntToStr(n)+":"+s1.SubString(s1.Pos(':')+1,2); w.last_time=w.start_time+s2;

//Відкриття файла для зчитування-записування f=FileOpen(s,fmOpenReadWrite);

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]