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

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

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

Файли

399

if((f=fopen("my_text.txt", "wt"))==0)

{ShowMessage("Файл не відкрито "); return; } for(int i=0; i<5; i++)

{for(int j=0; j<5; j++)

{// Перетворення елемента спочатку на AnsiString, потім на С-рядок. strcpy(s, FloatToStrF(a[i][j], ffFixed,6,2).c_str());

strcat(s,"\t");

// Долучення табуляції після чергового елемента,

fputs(s, f);

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

}

 

fputs("\n", f);

// і символу переведення курсора у кінець рядка

}

fclose(f); Memo1->Lines->LoadFromFile("my_text.txt");

}

Для виведення одновимірного масиву з текстового файла слід зчитати рядок і розбити його на числа, наприклад, за допомогою функції strtok():

char s[80];

char *D=" ";

// Пробіл – розділювач поміж числами у рядку.

char *p=new char [80]; // Рядок для числа у символьній формі.

fgets(s,80,f);

// Зчитування рядка,

p=strtok(s,D);

// і виокремлення першого числа.

while (p)

// Допоки рядок не є порожній,

{ a[i]=atof(p);

// перетворюється дійсне число з символьної форми,

i++;

// збільшується індекс елемента масиву

p=strtok(NULL,D);

// і виокремлюється наступне число.

}

 

Приклад 12.6 Заповнити матрицю 3 5 числами з текстового файла і обчислити суму елементів матриці.

Розв‟язок. У Блокноті слід створити текстовий файл з ім‟ям matr.txt, в якому записати елементи матриці. Форма з результатами роботи матиме вигляд

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

void __fastcall TForm1::Button1Click(TObject *Sender)

{float m[3][5]; FILE *f; char *s=new char[50]; int i=0, j; float sum=0; char *p=new char [50];

if((f=fopen("matr.txt", "rt"))==0)

400

Розділ 12

{ ShowMessage("Файл не відкрито "); return; }

while(fgets(s,50,f))

// У циклі зчитуються рядки з файла.

{ j=0;

// Розпочинаючи з нульового елемента,

p=strtok(s," ");

// виокремлюється перше число

while(p)

// і, допоки у рядку є числа,

{ m[i][j]=atof(p);

// перетворюється елемент з символьної форми,

j++;

// збільшується індекс стовпчика

p=strtok(NULL," ");// і виокремлюється наступне число

}

i++; // Збільшення індекса рядка (перехід до нового рядка матриці)

}

fclose(f);

for(int i1=0; i1<i; i1++) for(int j1=0; j1<j; j1++)

{ SG2->Cells[j1][i1] = FormatFloat("0.0",m[i1][j1]); sum+=m[i1][j1];

}

Edit1->Text=FormatFloat("0.0", sum);

}

12.2.3 Робота з текстовими файловими потоками у стилі С++

У С++ стандартна бібліотека містить три класи потоків для роботи з фай-

лами:

ifstream

вхідні файлові потоки (для зчитування)

ofstream

вихідні файлові потоки (для записування)

fstream

двонапрямлені файлові потоки (для зчитування та записування)

При роботі з файлами цих класів треба долучати до програми заголовний файл <fstream.h>.

Об‟єкти файлових потоків створюються за допомогою конструкторів відповідних класів, наприклад:

//Створення (відкриття) вихідного потоку (записування) ofstream outfile("Test.dat");

//Створення (відкриття) вхідного потоку (зчитування) ifstream fin ("Test.dat");

//Створення (відкриття) введення-виведення (записування і зчитування) fstream f_in_out("Test.dat");

До створених у такий спосіб потоків можна застосовувати операції “помістити в потік” (<<) і “взяти з потоку” (>>). Перевага цих операцій, які працюють з текстовими файлами, порівняно з розглянутими у попередніх розділах функціями, є простота використання і автоматичне розпізнавання типів даних. Розглянемо, наприклад, такий код:

int i=1, j=25, il, j1; double a=25e6, al; char s[40], sl[40];

strcpy(s, "Іванов");

ofstream fout ("Test.dat"); // Створення файла як вихідного потоку

Файли

401

if(!fout){ShowMessage("Файл не вдається створити"); return;} fout << i << ' ' << j << ' ' << а << ' ' << s <<endl;

fout.close();

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

ifstream fin("Test.dat");

// Відкриття файла як вхідного потоку

if(!fin) {ShowMessage("Файл не вдається відкрити"); return;}

fin >> i1 >>

j1 >> a1 >> s1;

fin.close();

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

Уцьому коді створюється файл з ім‟ям Test.dat, і до нього записуються

утекстовому вигляді два цілі числа – i та j, дійсне число а й рядок s, який містить одне слово, після чого маніпулятором потоку endl здійснюється перехід до нового рядка. Причому записування всіх цих даних здійснюється одним оператором, що містить зчеплені операції “помістити в потік”. Якщо порівняти це з аналогічними кодами, наведеними у попередніх підрозділах, то можна переконатися в компактності й простоті застосування цієї операції. Після того як файл

закриється, в ньому буде записано текст "1 25 2.5e+07 Іванов". Наступні команди створюють вхідний потік, пов‟язаний з цим файлом, і за допомогою операції “узяти з потоку” читають дані.

Як було зазначено раніш, файл треба описати. У С++ це можна здійснити в такий спосіб:

ofstream filename("C:\Test.dat", ios::out);

Тут у подвійних лапках зазначено ім‟я файла на диску C, а filename – ім‟я файлової змінної, тобто ім‟я, за допомогою якого здійснюватиметься відкривання файла у програмі. Якщо файл не існує, то його буде створено. Далі, після коми, слід зазначити режим відкривання файла (табл. 12.3).

Таблиця 12.3

 

Режими відкривання файлів

 

 

Режим

Значення режиму

 

 

ios::in

Відкрити файл для зчитування

ios::out

Відкрити файл для записування

ios::trunc

Вилучити дані з файла

ios::nocreate

Якщо файл не існує, відкривання файла не відбудеться

ios::ate

Після відкриття перемістити позицію зчитування-записування

 

на кінець файла

ios::app

Відкрити для записування даних в кінець файла

ios::binary

Відкрити файл, вважаючи його за бінарний (не текстовий)

Режими ios::ate та ios::app є схожими, оскільки переміщують позицію зчитування-записування (файловий вказівник) на кінець файла. Відмінність поміж ними полягає в тому, що режим ios::app дозволяє дописувати дані лише в кінець файла, в той час як режим ios::ate просто переміщує вказівник на кінець файла за аналогією роботи С-функції fseek(f,0,SEEK_END).

Імена констант відкриття є абревіатурами виконуваних дій: app – append (долучити), ate – at end (на кінець), trunc – trancate (урізати, відкинути) тощо.

402

Розділ 12

Режими відкриття файла за суттю є бітовими масками, а тому можна задавати два та більше режимів, поєднуючи їх операцією логічного “АБО”. Наприклад, щоб відкрити бінарний файл з ім‟ям Test.dat як для зчитування, так і для записування, слід записати функцію

fstream filename("Test.dat",ios::in|ios::out|ios::binary);

Тут символ “|” поєднує два режими ios::in та ios::out.

В табл. 12.4 наведемо відповідність поміж режимами відкривання в С++

та С.

 

 

Таблиця 12.4

Відповідність режимів відкривання файлів в С++ і С

 

 

 

Режим С++

Режим С

Значення режиму

 

 

 

ios::in

"r"

Відкрити файл для зчитування. Позиція встановиться

 

 

на початок файла

ios::out

"w"

Відкрити файл для записування. Позиція встановиться

ios::out |

 

на початок файла. Якщо файл існує, записування здій-

ios::trunc

 

снюватиметься поверх даних, тобто попередньо відбу-

 

 

деться урізання довжини файла до нуля

ios::out |

"а"

Відкрити файл для записування даних в кінець файла

ios::app

 

 

 

 

 

ios::in |

"r+"

Відкрити файл для зчитування-записування даних,

ios::out

 

починаючи з довільної позиції, тобто позиція може

 

 

змінюватись

ios::in |

"w+"

Відкрити файл для зчитування-записування, урізавши

ios::out |

 

довжину файла до нуля для існуючого файла

ios::trunc

 

 

Щоб перевірити правильність відкриття файла, можна скористатися умовним оператором

if(!filename)

{ ShowMessage("Неможливо відкрити файл!"); return; }

Завершення роботи з файлом здійснюється за допомогою функції filename.close();

12.2.4 Послідовне записування до файла і зчитування з файла

Для записування певної інформації до файла використовується операція “помістити в потік” (за аналогією з cout<<):

filename << блок1 << блок2 << ... << блоkN;

Для зчитування з файла певної інформації використовується операція “взяти з потоку” (за аналогією з cin>>)

filename >> блок1 >> блок2 >> ... >> блоkN;

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

Файли

403

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

getline (char*, int, char='\n');

Наприклад, оператор fin.getline(s, n);

читає з потоку не більше за n-1 символів й записує їх до змінної s.

Для роботи з файловим потоком слід до програми долучити бібліотеку

<fstream.h>.

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

Приклад 12.7 Увести інформацію про студентів: прізвище, найменування групи, рік народження. Переглянути весь список і вивести дані про студентів, яким на даний момент є понад 18 років.

Форма з результатами роботи матиме вигляд

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

#include <fstream.h>

#include <DateUtils.hpp> // Для функції визначення року YearOf()

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

void __fastcall TForm1::Button1Click(TObject *Sender) { char PIB[50], gr[10]; int god;

// Відкриття файла для записування даних у кінець файла ofstream output("dan.txt", ios::app);

if(!output) // Перевірка правильності відкриття файла для записування

{ ShowMessage("Файл не створено або не відкрито!"); return; } strcpy(PIB,Edit1->Text.c_str()); strcpy(gr,Edit2->Text.c_str());

god=StrToInt(Edit3->Text);

output<<PIB<<' '<< gr<<' '<<god<<endl; // Записування до файла output.close(); // Закриття файла Edit1->Clear(); Edit2->Clear(); Edit3->Clear(); Edit1->SetFocus();

}

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

void __fastcall TForm1::Button2Click(TObject *Sender)

{char *s=new char[80]; Memo1->Lines->Add("\t Дані з файла");

ifstream f("dan.txt"); // Відкриття файла для зчитування

404

Розділ 12

if(!f) { ShowMessage("Немає файла"); return;} while(f.getline(s,80)) Memo1->Lines->Add(AnsiString(s)); f.close(); // Закриття файла

}

// Кнопка “Студенти, яким є понад 18 років”

void __fastcall TForm1::Button3Click(TObject *Sender) { char PIB[50], gr[10]; int god;

Memo2->Clear();

fstream f("dan.txt",ios::in); // Відкриття файла для зчитування if(!f) { ShowMessage("Немає файла"); return;}

while(f >> PIB >> gr >> god) if(YearOf(Date())-god > 18)

Memo2->Lines->Add(String(PIB)+" "+String(gr)+" "+ IntToStr(god));

f.close();

}

12.2.5 Довільне записування до файла і зчитування з файла

Розглянемо варіант довільного записування і зчитування даних. Це означає, що записування даних до файла і зчитування цих даних здійснюватимуться з довільної зазначеної позиції файла. Позиціонування (встановлення курсора на певну позицію) виконується за допомогою методу seekp() класу ifstream при записуванні даних до файла, і за допомогою методу seekg() класу ofstream – при зчитуванні даних з файла. Ці методи мають два аргументи: перший зазначає на скільки байтів відносно позиції, зазначеної у другому аргументі, слід зрушитися. Існують такі константні значення позицій:

ios::beg – початок потоку (встановлюється за замовчуванням); ios::end – кінець потоку;

ios::cur – поточна позиція потоку. Наприклад:

f.seekg(n); – позиціонування на n-й байт від початку файла; f.seekg(n, ios::cur); – позиціонування на n-й байт уперед від поточ-

ної позиції;

f.seekp(k, ios::end); – позиціонування на k-тий байт від кінця файла; f.seekp(0, ios::end); – позиціонування на кінець файла.

Методи tellg() та tellp() повертають поточну позицію файла для потоків введення та виведення відповідно.

Для створювання текстових файлів довільного доступу існує багато способів. Мова С++ не накладає вимог щодо вмісту текстових файлів, але записані у файлі рядки мають бути однакової фіксованої довжини. Це надає можливість легко визначати точне місцезнаходження будь-якого рядка відносно початку файла. У такому файлі дані може бути записано в будь-яке місце та змінено без перезаписування всього файла. Для організації такого записування використовується маніпулятор setw(num), який задає довжину поля виведення з num по-

Файли

405

зицій. Для його використання слід долучити заголовний файл <iomanip.h>. Приклад його використання:

fout<<setw(15)<<name<<setw(20)<<surname<<setw(7)<<year<<endl;

Приклад 12.8 Записати до файла дані про студентів: прізвище, найменування групи, рік народження. Запрограмувати можливість змінювати у файлі той рядок (запис), номер якого зазначить користувач у текстовому полі (зауважимо, що рядки нумеруються з 0). Надати можливість переглядання файла, розпочинаючи з зазначеного запису, – номер запису теж задає користувач.

Розв‟язок. Це завдання є подібне до прикладу 12.7, але для його реалізації слід інакше організувати записування даних до файла. Зробимо це із використанням маніпулятора setw, який задає ширину виведення змінної у cout, (аналогічно до табуляції). Для використання маніпуляторів треба долучити до програми заголовний файл iomanip.h.

Форма проекту з результатами роботи матиме вигляд

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

#include <iomanip.h> #include <fstream.h>

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

void __fastcall TForm1::Button1Click(TObject *Sender) { char PIB[50], gr[10]; int god;

// Відкриття файла для записування даних у кінець файла ofstream output("dan.txt", ios::app);

if(!output) // Перевірка правильності відкриття файла для записування

{ ShowMessage("Файл не створено або не відкрито!"); return; } strcpy(PIB,Edit1->Text.c_str()); strcpy(gr,Edit2->Text.c_str());

god=StrToInt(Edit3->Text);

// Записування до файла з використанням маніпулятора setw output<<setw(15)<<PIB<<setw(8)<<gr<<setw(7)<<god<<endl; output.close(); // Закриття файла

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

}

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

void __fastcall TForm1::Button2Click(TObject *Sender) { char *s=new char[80];

406

Розділ 12

Memo1->Clear(); Memo1->Lines->Add("\t Дані з файла");

ifstream f("dan.txt"); // Відкриття файла для зчитування if(!f) { ShowMessage("Немає файла"); return;} while(f.getline(s,80)) Memo1->Lines->Add(AnsiString(s)); f.close(); // Закриття файла

}

// Кнопка “Замінити рядок файла із зазначеним номером

void __fastcall TForm1::Button3Click(TObject *Sender)

{char PIB[50], gr[10]; int god;

ofstream output("dan.txt",ios::in|ios::out); if(!output)

{ ShowMessage("Файл не створено або не відкрито!"); return; } int k=StrToInt(Edit4->Text);

// Уведення нових значень k-го рядка. strcpy(PIB,Edit1->Text.c_str()); strcpy(gr,Edit2->Text.c_str()); god=StrToInt(Edit3->Text);

output.seekp(k*32); // Позиціонування у k-тий рядок файла // Записування з використанням маніпулятора setw output<<setw(15)<<PIB<<setw(8)<<gr<<setw(7)<<god<<endl; output.close();

}

// Кнопка “Переглянути файл, розпочинаючи із зазначеного запису” void __fastcall TForm1::Button4Click(TObject *Sender) { int k=StrToInt(Edit5->Text); // Введення номера запису

char PIB[50], gr[10]; int god; Memo2->Clear();

Memo2->Lines->Add(" Дані з файла із запису № "+IntToStr(k)); ifstream fin("dan.txt");

if(!fin) { ShowMessage("Немає файла"); return;}

fin.seekg(k*32);

// Позиціонування у k-тий рядок файла

while(fin>>PIB>>gr>>god)

// Читаються дані з файла

Memo2->Lines->Add(String(PIB)+" "+String(gr)+" "+

IntToStr(god));

fin.close();

}

12.2.6 Опрацювання текстових файлів за допомогою дескрипторів При створюванні програм для Windows має сенс використовувати

стандартні функції цієї системи. Для роботи з ними не треба створювати спеціальні змінні файлового типу, оскільки в системі Windows кожен файл має власний дескриптор – унікальний ідентифікатор цілого типу. Англійською він зветься Handle і під такою назвою є присутній в прототипах багатьох функцій.

Основні функції С++ Builder, які використовуються для опрацювання файлів через дескриптори, наведено у табл. 12.5.

 

 

Файли

407

 

 

 

Таблиця 12.5

 

 

Функції опрацювання файлів через дескриптори

 

 

 

 

 

 

 

 

Формат

Опис функції

 

 

 

 

 

 

int FileCreate

Створює файл з ім‟ям FileName і повертає

 

 

(AnsiString FileName);

дескриптор створеного файла, який можна

 

 

 

 

в подальшому використовувати для роботи

 

 

 

 

з цим файлом. Якщо файл не вдалося

 

 

 

 

створити, функція повертає значення –1

 

 

int FileOpen

Відкриває файл з ім‟ям FileName у режимі Mode

 

(AnsiString FileName, un-

і повертає дескриптор відкритого файла

 

 

signed Mode);

 

 

 

void FileClose(int Handle);

Закриває файл з дескриптором Handle

 

 

int FileRead(int Handle,

Читає з файла з дескриптором Handle

 

 

void *Buffer,

данні з Buffer розміром Count байтів

 

 

unsigned Count);

 

 

і повертає значення прочитаних байтів;

 

 

 

 

 

 

 

 

у разі помилки повертає –1

 

 

int FileWrite(int Handle,

Записує до файла з дескриптором Handle

 

 

void *Buffer,

данні з Buffer розміром Count байтів

 

 

unsigned Count);

 

 

і повертає значення записаних байтів;

 

 

 

 

 

 

 

 

у разі помилки повертає –1

 

 

int FileSeek(int Handle,

Переміщує поточну позицію файла на Offset

 

 

int Offset, int Origin);

байтів відносно позиції визначеної параметром

 

 

 

Origin, можливі значення якого

 

 

 

 

розглянуто нижче

 

 

 

bool DeleteFile (An-

Видаляє файл з ім‟ям FileName. Якщо файл

 

 

 

siString FileName);

не можливо видалити чи він не існує, функція

 

 

 

повертає значення false, інакше – true

 

 

 

bool RenameFile

Перейменовує файл, тобто змінює ім‟я OldName

 

 

(AnsiString OldName,

на NewName. Якщо перейменувати файл

 

 

 

AnsiString NewName);

 

 

 

не вдалося, функція повертає значення false,

 

 

 

 

 

 

інакше – true

 

 

 

Розглянемо ці функції більш докладно на прикладах.

 

 

 

Файл створюється за допомогою функції FileCreate(), наприклад:

 

 

 

int f = FileCreate("info.txt");

 

 

Режими створювання і відкривання файла у Windows відрізняються. Функція FileCreate() повертає ідентифікатор файла (ціле додатне число) або значення –1, якщо створити файл не вдалось. Значення результату –1 свідчить про помилку для більшості функцій Windows.

Відкривання файла виконується функцією

int FileOpen(const AnsiString FileName, unsigned Mode);

Параметр FileName містить ім‟я файла, можливо, із зазначенням місця розташування на диску цього файла.

408

Розділ 12

Режим відкривання визначається параметром Mode, який може набувати одне з таких константних значень:

fmOpenRead – відкрити лише для зчитування з файла;

fmOpenWrite – відкрити лише для записування у файл;

fmOpenReadWrite – відкрити і для зчитування і для записування (режим редагування файла);

fmCreate – за умови наявності файла, відкрити його для записування, інакше – створити новий файл. На відміну від інших констант, які означено в модулі <SysUtils >, цю константу означено у <classes.h>.

Наприклад, команда

int f = FileOpen("info.txt", fmOpenWrite);

відкриє файл з ім‟ям info.txt у режимі записування даних. Закрити цей файл можна за допомогою функції

FileClose(f);

Для переміщення поточної позиції файла застосовують функцію int FileSeek(int Handle, int Offset, int Origin);

Тут параметр Offset задає зсув у байтах відносно позиції визначеної парамет-

ром Origin.

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

Спосіб відліку

0

від початку файла

1

від поточної позиції

2

від кінця файла

Наприклад, функція

 

FileSeek(f,0,0);

 

встановить поточну позицію на початок файла, а функція

FileSeek(f,1,1);

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

Розглянемо приклад відкриття файла з ім‟ям "test.txt", переміщення позиції на кінець файла і дописування рядка st:

int f; // Оголошення дескриптора файла

AnsiString st="Цей рядок буде дописано в кінець файла"; f=FileOpen("test.txt", fmOpenWrite);

//Відкриття файла для зчитування і записування FileSeek(f,0,2); // Встановлення позиції на кінець файла

FileWrite(f, st.c_str(),st.Length());

//Записування рядка st довжиною st.Length() до файла

FileClose(f);

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

Відкрити файл для зчитування можна в такий спосіб: f = FileOpen("test.txt", fmOpenReadWrite);

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