Урок №221. Рандомний файловий ввід і вивід

  Юрій  | 

  Оновл. 13 Тра 2021  | 

 127

На цьому уроці ми розглянемо реалізацію рандомного файлового вводу/виводу в мові С++.

Файловий вказівник

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

Рандомний доступ до файлів за допомогою функцій seekg() і seekp()

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

Рандомний доступ до файлу здійснюється шляхом маніпулювання файловим вказівником за допомогою функції seekg() (закінчення “g” = get”, тобто “отримати/дістати”) — для вводу, і функції seekp() (закінчення “p” = put” (тобто “покласти/помістити”) — для виводу.

Функції seekg() і seekp() приймають наступні два параметри:

   перший параметр — це зміщення на яке слід перемістити файловий вказівник (вимірюється в байтах);

   другий параметр — це флаг ios, який позначає місце, від якого слід відштовхуватися при виконанні зміщення.

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

   beg — зміщення відносно початку файлу (за замовчуванням);

   cur — зміщення відносно поточного розташування файлового вказівника;

   end — зміщення відносно кінця файлу.

Позитивне зміщення означає переміщення файлового вказівника в бік кінця файлу, тоді як негативне зміщення означає переміщення файлового вказівника в бік початку файлу. Наприклад:

Переміщення в початок або в кінець файлу:

Тепер давайте поєднаємо функцію seekg() з файлом SomeText.txt (який ми використовували на попередньому уроці).

Вміст файлу SomeText.txt:

See line #1!
See line #2!
See line #3!
See line #4!

Код програми:

Результат виконання програми:

ne #1!
#2!
See line #4!

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

Є ще дві інші корисні функції — tellg() і tellp(), які повертають абсолютну позицію файлового вказівника. Це корисно при визначенні розміру файлу:

Результат:

56

Це ми отримали розмір файлу SomeText.txt в байтах.

Одночасне читання і запис в файл за допомогою fstream

Клас fstream (майже) здатний одночасно читати вміст файлу і записувати дані в нього! Нюанс полягає в тому, що ви не можете довільно переключатися між читанням і записом файлу. Як тільки почнеться читання або запис файлу, то єдиним способом переключитися між читанням або записом буде виконання операції, яка змінить поточний стан файлового вказівника (наприклад, пошук даних). Якщо ви не хочете переміщати файловий вказівник (бо він вже знаходиться в потрібному місці), то ви можете просто виконати пошук поточних даних (на які вказує файловий вказівник):

Тепер давайте напишемо програму, яка відкриє файл, прочитає його вміст і замінить всі знайдені голосні літери на символ #:

Результат виконання програми (вміст файлу SomeText.txt):

S## l#n# #1!
S## l#n# #2!
S## l#n# #3!
S## l#n# #4!

Інші корисні методи класів файлового вводу/виводу в мові C++:

   remove() — видаляє файл;

   is_open() — повертає true, якщо потік в даний момент відкритий, і false — якщо закритий.

Попередження про запис вказівників в файли

Хоча записувати змінні в файл досить просто, все стає складнішим, коли ми починаємо працювати з вказівниками. Як ми вже знаємо, вказівник містить лише адресу змінної, на яку він вказує. Хоча ці адреси можна записувати в файл і зчитувати їх з файлу — це може спричиняти проблеми, тому що адреса однієї і тієї ж змінної може відрізнятися при кожному повторному запуску програми. Відповідно, хоча змінна могла перебувати за адресою 003AFCD4, коли ви записували цю адресу на диск (в будь-який файл), при повторному запуску програми вона вже може знаходитися за іншою адресою!

Наприклад, припустимо, що у нас є змінна someValue типу int, яка знаходиться за адресою 003AFCD4. Ми присвоюємо someValue значення 7. Потім оголошуємо вказівник *pnValue, який вказує на someValue (адресою someValue є 003AFCD4). Ми записуємо значення 7 і значення pnValue (003AFCD4) в будь-який файл.

Через кілька тижнів ми знову запускаємо цю програму і намагаємося вилучити значення з файлу. Ми вилучаємо значення 7 в змінну someValue, яка в поточній програмі вже знаходиться за адресою 0034FD90. Далі ми вилучаємо адресу 003AFCD4 у вказівник *pnValue. Оскільки pnValue вказує на 003AFCD4, а someValue знаходиться за адресою 0034FD90, то pnValue більше не вказує на someValue, і спроба доступу до значення адреси, яку зберігає pnValue, призведе до неприємностей.

Правило: Не зберігайте адреси змінних в файлах. Змінні, які спочатку знаходилися по одним адресам, при повторному запуску програми можуть перебувати вже за іншими адресами.

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (1 оцінок, середня: 5,00 з 5)
Loading...

Залишити відповідь

Ваш E-mail не буде опублікований. Обов'язкові поля відмічені *