Урок №213. Рандомный файловый ввод/вывод

  Юрий Ворон  | 

    | 

  Обновл. 9 Фев 2019  | 

 408

В этом уроке мы рассмотрим реализацию рандомного файлового ввода/вывода в С++.

Файловый указатель

Каждый класс файлового ввода/вывода содержит файловый указатель, который используется для отслеживания текущей позиции чтения/записи данных в файл. Любая запись в файл или чтение содержимого файла происходит в текущем местоположении файлового указателя. По умолчанию, при открытии файла для чтения или записи, файловый указатель находится в самом начале этого файла. Однако, если файл открывается в режиме добавления, то файловый указатель перемещается в конец файла, чтобы пользователь имел возможность добавить данные в файл, а не перезаписать его.

Рандомный доступ к файлам с помощью seekg() и seekp()


До этого момента мы осуществляли последовательный доступ к файлам, т.е. выполняли чтение/запись файла по порядку. Тем не менее, мы можем выполнить и произвольный (рандомный) доступ к файлу, т.е. перемещаться по файлу, как захотим. Это может быть полезно, когда файл имеет обширное содержимое, а нам нужна всего лишь небольшая конкретная запись из всего этого. Вместо последовательного доступа (когда мы переходим до нужной записи начиная с самого начала файла), мы можем осуществить непосредственный доступ к этой записи.

Рандомный доступ к файлу осуществляется путём манипулирования файловым указателем с помощью методов seekg() (для ввода) и seekp() (для вывода). В seekg() окончание g = “get” (т.е. «получить/достать»), в seekp() окончание p = «put» (т.е. «положить/поместить»).

Методы seekg() и seekp() принимают следующие два параметра:

   первый параметр – смещение на которое следует переместить файловый указатель (измеряется в байтах);

   второй параметр – флаг ios, который обозначает место, от которого следует отталкиваться при смещении.

Флаги ios, которые принимают методы seekg() и seekp() в качестве второго параметра:

   beg – cмещение относительно начала файла (по умолчанию);

   cur – cмещение относительно текущего местоположения файлового указателя;

   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 (9 оценок, среднее: 4,56 из 5)
Загрузка...

Добавить комментарий

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