Короткі теоретичні відомості

2.1. Потоковий ввод-вивід в стилі С

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

В С існують засоби введення-виведення. Всі операції введення-виведення реалізуються за допомогою функцій, які знаходяться в бібліотеці С. Бібліотека С підтримує три рівні введення-виведення:

• потоковий ввід-висновок;

• введення-виведення нижнього рівня;

• введення-виведення для консолі і портів (залежить від ОС).

Потік - це абстрактне поняття, що відноситься до будь переносу даних від джерела до приймача.

Читання даних з потоку називається витяганням, висновок у потік - приміщенням, або включенням.

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

выходной поток
буфер
буфер
входной поток
программа
Чтение из потока
Помещение в поток

Рисунок 25- Читання даних з потоку

При роботі з потоком можна:

• Відкривати і закривати потоки (пов'язувати покажчики на потік з конкретними файлами);

• вводити і виводити рядок, символ, відформатовані дані, порцію даних довільної довжини;

• аналізувати помилки вводу-виводу і досягнення кінця файлу;

• управляти буферизацією потоку і розміром буфера;

• отримувати і встановлювати покажчик поточної позиції у файлі.

Функції бібліотеки введення-виведення знаходяться в заголовному файлі <stdio.h>.

Перш ніж почати працювати з потоком, його треба ініціювати, тобто відкрити. При цьому потік пов'язується зі структурою зумовленого типу FILE, визначення якої знаходиться в бібліотечному файлі <stdio.h>. У структурі знаходиться покажчик на буфер, покажчик на поточну позицію файлу і т. п. При відкритті потоку, повертається покажчик на потік, тобто на об'єкт типу FILE.

# include <stdio.h>;

........

FILE * fp;

............

fp = fopen ("t.txt", "r");

де fopen (<ім'я файлу>, <режім_откритія>) - функція для ініціалізації файлу.

Існуючи режими для відкриття файлу представлені в таблиці

Таблиця 25 – Режими для відкриття файлу

Режим Описание режима открытия файла
r Файл открывается для чтения, если файл не существует, то выдается ошибка при исполнении программы.
w Файл открывается для записи, если файл не существует, то он будет создан, если файл уже существует, то вся информация из него стирается.
a Файл открывается для добавления, если фай не существует, то он будет создан, если существует, то информация из него не стирается, можно выполнять запись в конец файла
r+ Файл открывается для чтения и записи, изменить размер файла нельзя, если файл не существует, то выдается ошибка при исполнении программы.
w+ Файл открывается для чтения и записи, если файл не существует, то он будет создан, если файл уже существует, то вся информация из него стирается.
a+ Файл открывается для чтения и записи, если фай не существует, то он будет создан, если существует, то информация из него не стирается, можно выполнять запись в конец файла

Потік можна відкрити в текстовому (t) або довічним (b) режимі. За замовчуванням використовується текстовий режим. У явному вигляді режим вказується таким чином:

• "r + b" або "rb" - двійковий (бінарний) режим;

• "r + t" або "rt" - текстовий режим.

У файлі stdio.h визначена константа EOF, яка повідомляє про закінчення файлу (негативне ціле число).

При відкритті потоку можуть виникати такі помилки:

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

• диск заповнений (при записі);

• диск захищений від запису (при записі) і т. п.

У цих випадках покажчик на потік придбає значення NULL (0). Покажчик на потік, відмінний від аварійного не дорівнює 0.

Для виведення про помилку при відкритті потоку використовується стандартна бібліотечна функція з файлу <stdio.h>

void perror (const char * s);

if ((fp = fopen ("t.txt", "w") == NULL)

{

/ / Виводить рядок символів з повідомленням про помилку

perror ("\ nошібка при відкритті файлу");

exit (0);

}

Після роботи з файлом, його треба закрити

fclose (<указатель_на_поток>);

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

• стандартний потік вводу (stdin);

• стандартний потік виводу (stdout);

• стандартний потік виводу про помилки (stderr).

За замовчуванням stdin ставиться у відповідність клавіатура, а потокам stdout і stderr - монітор. Для введення-виведення за допомогою стандартних потоків використовуються функції:

• getchar () / putchar () - введення-виведення окремого символу;

• gets () / puts () - ввід-вивід рядка;

• scanf () / printf () - форматований ввід / вивід.

Аналогічно роботі зі стандартними потоками виконується введення-виведення в потоки, пов'язані з файлами.

Для символьного введення-виведення використовуються функції:

• int fgetc (FILE * fp), де fp - покажчик на потік, з якого виконується зчитування. Функція повертає черговий символ у формі int з потоку fp. Якщо символ не може бути прочитаний, то повертається значення EOF.

• int fputc (int c, FILE * fp), де fp - покажчик на потік, в який виконується запис, c - змінна типу int, в якій міститься записуваний в потік символ. Функція повертає записаний в потік fp символ у формі int. Якщо символ не може бути записаний, то повертається значення EOF.

Для рядковому введення-виведення використовуються наступні функції:

• char * fgets (char * s, int n, FILE * f), де char * s - адреса, за якою розміщуються лічені байти, int n - кількість лічених байтів, FILE * f - покажчик на файл, з якого виробляється зчитування.

Прийом байтів закінчується після передачі n-1 байтів або при отриманні керуючого символу '\ n'. Керуючий символ теж передається в приймаючу рядок. Рядок у будь-якому випадку закінчується '\ 0'. При успішному завершенні роботи функція повертає покажчик на прочитану рядок, при неуспішному - 0.

• int puts (char * s, FILE * f), де char * s - адресу, з якого беруться записувані в файл байти, FILE * f - покажчик на файл, в який проводиться запис.

Символ кінця рядка ('\ 0') в файл не записується. Функція повертає EOF, якщо при записі у файл сталася помилка, при успішній запису повертає невід'ємне число.

Для блокового вводу-виводу використовуються функції:

• int fread (void * ptr, int size, int n, FILE * f), де void * ptr - вказівник на область пам'яті, в якій розміщуються лічені з файлу дані, int size - розмір одного зчитуваного елемента, int n - кількість прочитуваних елементів, FILE * f - покажчик на файл, з якого виробляється зчитування.

У разі успішного зчитування функція повертає кількість лічених елементів, інакше - EOF.

• int fwrite (void * ptr, int size, int n, FILE * f), де void * ptr - вказівник на область пам'яті, в якій розміщуються лічені з файлу дані, int size - розмір одного записуваного елемента, int n - кількість записуваних елементів, FILE * f - покажчик на файл, в який проводиться запис.

У разі успішної запису функція повертає кількість записаних елементів, інакше - EOF.

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

• int fprintf (FILE * f, const char * fmt,...), де FILE * f - покажчик на файл, в який проводиться запис, const char * fmt - форматна рядок,... - Список змінних, які записуються в файл.

Функція повертає число записаних символів.

• int fscanf (FILE * f, const char * fmt, par1, par2,...), де FILE * f - покажчик на файл, з якого проводиться читання, const char * fmt - форматна рядок, par1, par2,... - Список змінних, в які заноситься інформація з файлу.

Функція повертає число змінних, яким присвоєно значення.

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

• int fseek (FILE * f, long off, int org), де FILE * f - покажчик на файл, long off - позиція зсуву, int org - початок відліку.

Зсув задається вираз або змінної і може бути негативним, тобто можливе переміщення як у прямому, так і в зворотному напрямках. Початок відліку задається однією із визначених у файлі <stdio.h> констант:

SEEK_SET == 0 - початок файлу;

SEEK_CUR == 1 - поточна позиція;

SEEK_END == 2 - кінець файлу.

Функція повертає 0, якщо переміщення в потоці виконано успішно, інакше повертає ненульове значення.

2.2. Обробка елементів файлу

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

void del (char * filename)

{/ / Видалення запису з номером х

FILE * f;/ / початковий файл

FILE * temp;/ / допоміжний файл

/ / Відкрити вихідний файл для читання

f = fopen (filename, "rb");

/ / Відкрити допоміжний файл для запису

temp = fopen ("temp", "wb")

student a;/ / буфер для читання даних з файлу

/ / Зчитуємо дані з вихідного файлу в буфер

for (long i = 0; fread (& a, sizeof (student), 1, f); i + +)

if (i! = x) / / якщо номер запису не дорівнює х

{

/ / Записуємо дані з буфера в тимчасовий файл

fwrite (& a, sizeof (student) 1, temp);

}

else

{

cout << a << "- is deleting...";

}

fclose (f);/ / закриваємо вихідний файл

fclose (temp); / / закриваємо тимчасовий файл

remove (filename);/ / видаляємо вихідний файл

rename ("temp", filename);/ / перейменовуємо тимчасовий файл

}

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

Для додавання елементів в початок або в середину файлу також використовується допоміжний файл, в який в потрібне місце додаються нові дані.

Для додавання елементів кінець файлу достатньо відкрити його в режимі "a" або "a +" (для додавання) і записати нові дані в кінець файлу.

f = fopen (filename, "ab");/ / відкрити файл для додавання

cout << "\ nHow many records would you add to file?";

cin >> n;

for (int i = 0; i <n; i + +)

{

/ / Прочитати об'єкт

fwrite (& a, sizeof (student), 1, f);/ / записати в файл

}

fclose (f);/ / закрити файл

2.3. Потоковий ввод-вивід в стилі С + +

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

По напрямку обміну потоки можна розділити на

• вхідні (дані вводяться в пам'ять),

• вихідні (дані виводяться з пам'яті),

• двонаправлені (допускающие як витяг, так і включення).

По виду пристроїв, з якими працює потік, потоки можна розділити на стан-дротяні, файлові і рядкові:

• стандартні потоки призначені для передачі даних від клавіатури і на екран дисплея,

• файлові потоки - для обміну інформацією з файлами на зовнішних носіях даних (наприклад, на магнітному диску),

• рядкові потоки - для роботи з масивами символів в оперативній пам'яті.

Для роботи зі стандартними потоками бібліотека C + + містить бібліотеку <iostream.h>. При цьому в програмі автоматично стають доступними об'єкти:

• cin - об'єкт, відповідає стандартному потоку вводу,

• cout - об'єкт, відповідає стандартному потоку виводу.

Обидва ці потоку є буферізірованний.

Форматований ввід / вивід реалізується через дві операції:

• << - висновок в потік;

• >> - читання з потоку.

Використання файлів в програмі передбачає такі операції:

• створення потоку;

• відкриття потоку і зв'язування його з файлом;

• обмін (введення / виведення);

• знищення потоку;

• закриття файлу.

Для роботи зі файловими потоками бібліотека C + + містить бібліотеки:

• <ifstream.h> - для роботи з вхідними потоками,

• <ofstream.h> - для роботи з вихідними потоками

• <fstream.h> - для роботи з двонаправленими потоками.

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

Для створення файлового потоку використовуються спеціальні методи-конструктори, які створюють потік відповідного класу, відрозкривають файл з вказаним ім'ям і пов'язують файл з потоком:

• ifstream (const char * name, int mode = ios:: in);/ / вхідний потік

• ofstream (const char * name, int mode = ios:: out | ios:: trunc);/ / вихідний потік

• fstreamCconst char * name, int mode = ios:: in | ios:: out);/ / двонаправлений потік

Другим параметром є режим відкриття файлу. Замість значення за замовчуванням можна вказати одне з наступних значень, визначених в класі ios.

Таблиця 26 – Методи-конструктори

ios::in открыть файл для чтения;
ios::out открыть файл для записи;
ios::ate установить указатель на конец файла, читать нельзя, можно только записывать данные в конец файла;
ios::app открыть файл для добавления;
ios::trunc если файл существует, то создать новый;
ios::binary открыть в двоичном режиме;
ios::nocreate если файл не существует, выдать ошибку, новый файл не открывать
ios::noreplace если файл существует, выдать ошибку, существующий файл не открывать;

Відкрити файл у програмі можна з використанням або конструкторів, або методу open, має такі ж параметри, як і у відповідному конструкторі.

fstream f; / / створює файловий потік f

/ / Відкривається файл, який зв'язується з потоком

f.open (".. \ \ f.dat", ios:: in);

/ / Створює і відкриває для читання файловий потік f

fstream f (".. \ \ f.dat", ios:: in);

Після того як файловий потік відкритий, працювати з ним можна також як і зі стандартними потоками cin і cout. При читанні даних з вхідного файлу треба контролювати, чи був досягнутий кінець файлу після чергової операції виводу. Це можна робити за допомогою методу eof ().

Якщо в процесі роботи виникне помилкова ситуація, то потоковий об'єкт набуває значення рівне 0.

Коли програма залишає область видимості потокового об'єкта, то він знищується, при цьому перестає існувати зв'язок між потоковим об'єктом і фізичним файлом, а сам файл закривається. Якщо файл потрібно закрити раніше, то використовується метод close ().

/ / Створення файлу з елементів типу person

struct person

{

char name [20];

int age;

};

person * mas;/ / динамічний масив

fstream f ("f.dat", ios:: out);/ / двонаправлений файловий потік

int n;

cout << "N?";

cin >> n;

mas = new person [n];/ / створюємо динамічний масив

for (int i = 0; i <n; i + +)

{

cout << "?";

/ / Введення одного елемента типу person зі стандартного потоку cin

cin >> mas [i]. name;

cin >> mas [i]. age;

}

/ / Запис елементів масиву в файловий потік

for (i = 0; i <n; i + +)

{

f << mas [i]. name; f << "\ n";

f << mas [i]. age; f << "\ n";

}

f.close ();/ / закриття потоку

/ / Читання елементів з файлу

person p;

f.open ("f.dat", ios:: in);/ / відкриваємо потік для читання

do

{/ * Читаємо елементи типу person з файлового потоку f в змінну p * /

f >> p.name;f >> p.age;

/ / Якщо досягнуто кінець файлу, виходимо з циклу

if (f.eof ()) break;

/ / Вивід на екран

cout << p.name << "" << p.age << "\ n";

} While (! F.eof ());

f.close ();/ / закриття потоку


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: