
C _Учебник_МОНУ
.pdf
Файли |
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"); // Відкриття файла для зчитування

Файли |
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];
|
|
Файли |
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);