Как уже отмечалось, С++ позволяет выполнять позиционирование в любых потоках, кроме стандартных. Методы позиционирования приведены в табл. 14.5. Методы позиционирования различаются для входных и выходных потоков: для входных потоков (см. п. п. 27.6.1.3 в [1]) имена методов заканчиваются символом «g» (от слова get), для выходных (см. п. п. 27.6.2.4 в [1]) — символом «р» (от слова put).
Таблица 14.5. Методы позиционирования
Метод Описание
postype tellg()
istream& seekg (postype p)
istream& seekg (off_type p, ios::seekdir s)
postype tellp()
istream& seekp (postype p)
istream& seekp (offtype p, ios::seekdir s)
Получить текущую (абсолютную) позицию чтения
Установить абсолютную позицию чтения
Установить относительную позицию чтения
Получить текущую (абсолютную) позицию записи
Установить абсолютную позицию записи Установить относительную позицию записи
Функции tellp()Htellg() возвращают абсолютное смещение от начала потока. Символы потока нумеруются, как и индексы массива, начиная с нуля. Однако тип pos_type не является целым типом. Поэтому получать текущую позицию чтения в файловом потоке нужно так:
ios::pos_type р = file.tellgO;
Можно сохранить текущую позицию и в переменной типа streampos, например: streampos pos = f ile. tellpO ;
Переход к позиции, сохраненной в переменной pos, делается так: file.seekp(pos);
Несмотря на то что тип значения позиции в потоке не является целым значением, целые константы можно использовать в качестве аргумента в методах установки позиции — выполняется преобразование по умолчанию. Символы в потоке нумеруются, начиная с нулевого, поэтому позиционирование в начало потока можно выполнить, например, так:
file.seekg(0);
После этого опять можно будет прочитать первый символ потока.
Методы установки относительной позиции очень похожи на функцию f seek() из библиотеки <cstdio>. В классе ios_base определены следующие константы (см. п. п. 27.4.2.1.5 в [1]):
static const seekdir beg, // позиционирование от начала потока
сиг, // позиционирование от текущей позиции end; // позиционирование от конца потока
Первый аргумент, который обычно задается целым числом (выполняется преобразование по умолчанию), является смещением (в символах) от указанной позиции. Положительное число означает смещение вперед — ближе к концу файла, отрицательное — смещение назад, к началу файла. Например, позиционироваться в конец файла можно так:
file.seekg(0, ios::end);
При позиционировании нужно следить, чтобы позиция оставалась внутри файла. Попытка позиционироваться до начала потока или после конца приводит поток в состояние bad(). Небольшой пример1 показывает, как работает относительное позиционирование (листинг 14.22).
Листинг 14.22. Относительное позиционирование int main()
{ fstream strm("с:/binfiles/oonumber2.bin",ios::binary|ios::in|ios::out); int a = 0;
// читаем числа из файла и выводим
strm.seekg(2*sizeof(int), ios::beg); // выставляемся от начала
strm.read((char *)&a, sizeof(int)); // читаем
cout << a << ' ' << '\n' ;
// записываем новое число на ту же позицию
а = 12021;
strm.seekp(2*sizeof(int), ios::beg); // выставляемся от начала
strm.write((char *)&а, sizeof(int)); // записываем
// возвращаемся, читаем снова и выводим
strm.seekg(2*sizeof(int), ios::beg); // выставляемся от начала
strm.read((char *)&a, sizeof(int)); // опять читаем
cout << a << ' ' << '\n'; return EXIT_SUCCESS;
}
В примере открывается существующий (см. листинг 14.7) двоичный файл в режиме чтения/записи. Оба флага (ios:: in | ios::out) задавать обязательно, иначе по умолчанию файловый поток открывается только для записи. Сначала читается третье от начала число, затем выполняется возврат в ту же позицию и записывается новое число. Результат можно наблюдать на экране.
С помощью методов позиционирования можно вычислить размер файла (листинг 14.23).
Листинг 14.23. Функция, вычисляющая длину файла long filesize(ifstream &stream)
{ streampos curpos = stream. tellgO ; // сохраняем текущую позицию
stream.seekg(0, ios::end); // выставляемся на конец файла
streampos last = stream.tellgO; // получаем позицию = дпина
stream.seekg(curpos); // возвращаем текущую позицию,
return last;
}
Несмотря на то что используются переменные типа streampos, в результате выполняется преобразование по умолчанию в тип long. Применять эту функцию можно как с двоичными файлами, так и с текстовыми, например:
ifstream g;
g.open("с:/textfiles/result.files"); cout << filesize(g) << endl; g.closeO ;
g.open("c:/binfiles/bin2.bin", ios::binary); cout << filesize(g) << endl; g.closeO;
Сначала вычисляется длина текстового файла result.files, который образовался в каталоге textfiles в результате вызова функции includeFi 1е() (см. листинг 14.5). Затем с помощью той же функции вычисляется длина двоичного файла из листинга 14.8.
Методы позиционирования облегчают обработку двоичных файлов, записанных любыми другими средствами, например средствами языка Pascal. В С++, в отличие от языка программирования Pascal, отсутствуют типизированные файлы с записями (file of type). В языке Pascal такие файлы являются двоичными. Записи нумеруются, начиная с нуля, что обеспечивает прямой доступ к записи. Такие файлы в программе на языке Pascal по умолчанию открываются в режиме чтения/записи.
Мы вполне можем обработать такие файлы и обеспечить прямой доступ к записям по номерам средствами С++. Для этого нужно объявить в программе структуру, имеющую поля соответствующих типов, заданные в том же порядке, что в записи в программе на Pascale. Здесь нужно внимательно отнестись к проблеме выравнивания — структура в С++ должна иметь точно такой же размер, как и запись в Pascal. Далее нужно открыть файл как двоичный в режиме чтения/записи. Чтение из файла выполняется оператором
strm.read((char *)&record, sizeof(Т));
А запись выполняется оператором
strm.write((char *)&record, sizeof(Т));
Здесь sizeof (Т) — размер структуры в С++, который должен совпадать с размером записи в Pascal.
Методы относительного позиционирования позволяют реализовать прямой доступ к записям этого двоичного файла. Например, позиционирование на k-ю запись для чтения выполняется оператором
strm.seekg(k*sizeof(Т), beg);
Если необходимо сместиться вперед от текущей позиции на одну запись, можно воспользоваться любой из следующих инструкций:
// эквивалентные вызовы seekg
stream.seekg(stream.tellgO + sizeof(T)); // абсолютная позиция
stream.seekg(sizeof(Т), ios::cur); // относительная позиция
Сместиться назад на одну запись можно так:
stream.seekg(stream.tellgO - sizeof(Т)); // абсолютная позиция
stream.seekg(-sizeof(T), ios::cur); // относительная позиция
|