Программирование >>  Унарные и бинарные операторы 

1 ... 30 31 32 [ 33 ] 34 35 36 37


ная. Копировать по байту за раз - большая расточительность. Поэтому создадим напоследок еще одну команду копирования, основанную на функциях read() и writeO, способных читать сразу несколько байтов (листинг 12.10).

Листинг 12.10

#include <fstream>

#define BSIZE 100

using namespace std:

int maindnt argc. char **argv){

char buf[BSIZE]:

int n;

ifstream in(argv[l].ios::in ios::binary): ofstream out(argv[2]. ios::out ios::binary): while(l){

in.read(buf.BSIZE); n=in.gcount(); if(n-BSIZE)

out.write(buf.BSIZE); else{

out.write(buf.n): in.closeO; out.closeO: break:

return 0: }

В программе из листинга 12.10 за функцией чтения сразу следует функция gcountC), проверяющая, сколько байтов на самом деле прочитано. Если это число равно размеру буфера, байты переписываются в выходной файл. Меньшее число байтов означает, что достигнут конец файла, и нужно дописать остаток в выходной файл, закрыть оба файла и завершить программу.

Задача 12.4. Сравните скорости работы программ из листингов 12.9 и 12.10 при копировании длинного (гораздо больше размеров буфера) файла.

Если скомпилировать эту программу и проверить па реальных файлах, окажется, что работает она весьма странно. Исходные тексты на С++ копируются верно, а от исполняемых файлов (с расширением .ехе) остается только обрывок начала

Все дело в том, что файлы по умолчанию открываются в так называемом текстовом режиме. Это значит, что некоторые служебные символы при чтении меняются, а другие (например, символ SUB, кодируемый числом 26) вообще означают конец ввода. Символа SUB не бывает в текстовых файлах, но он часто встречается в исполняемых. Вот почему их копирование не удается.

Чтобы выйти из затруднения, в С++ предусмотрена специальная константа ios::binary, которая устанавливает бинарный режим чтения и записи файлов. В нем копирование байтов происходит безоглядно до самого конца файла. Правильная программа копирования показана в листинге 12.9.

Листинг 12.9

#include <fstream>

using namespace std:

int main(int argc. char **argv){

char ch;

fstream in(argv[l]. ios::in ios::binary); fstream out(argv[2].ios::out ios::binary): whne(in.get(ch))

out.put(ch): return 0: }

Задача 12.3. Перепишите программу из листрк1га 12.9 так, чтобы она проверяла, достаточно ли параметров в командной строке, существуют ли копируемый файл и файл-копия. Если выходной файл уже существует, нужно предупреждать о том, что он будет уничтожен.

Программа из листинга 12.9 работает правильно, но обладает одним недостатком. Она очень медлен-



226 Глава 12. Ввод-вывод

Непоследовательные файлы 227

using namespace std; int main(){ char ch;

fstream fCtesf.ios; ;inios: :out); f 01234567B9 ; f.seekg(5.ios::beg); f.get(ch);

cout ch end!; 5

f.seekg(-5.ios::end);

f.putCA);

f.seekg(-l.ios::cur):

f.get(ch):

cout ch end! -.Ilk

return 0;

Сначала в файл f, открытый для чтения и записи, номспщются символы О, 1,2, 3, 4, 5, 6, 7, 8 и 9. Затем функция f .seekg(5. ios: :beg) перемеп1ает флаг на пятую позицию от его начала (о том, что перемещение идет относительно нача-ча, говорит константа ios;:beg). На этой позиции стоит символ 5, KOTopbiii и оказывается в переменной ch после чтения функцией f .get(ch). Сле-дуюп1ая функция, f.seekg(-5. ios: :end), перемещает флаг на пять позиций от конца файла. На это указывает константа ios: rend. При перемсп1еннях флага от конца файла нулевой считается позиция сразу за последним символом. Так что флаг снова указывает на символ 5, и функция f .put( А ) записывает на место пятерки букву А . После записи флаг передвигается на шаг вдоль файла, указывая на шестую позицию, где стоит символ 6. Чтобы прочесть только что записанный символ, необходимо вернуться па шаг назад, что и делает функция f.seekg(-l.ios: ;сиг) (константа ios: :cur говорит о перемещении относительно текущей позиции). После ЭТ01Х) флаг указывает на символ Л , который и появится на экране.

Следя за перемещением флага, нужно понимать, что он сдвигается ие обязательно на одну позицию. Если

Непоследовательные файлы

До сих пор мы перемещались по файлам, как по дорогам с односгороппим движением - только вперед. Прочитав пулевой символ, можно было прочитатьто;п>-ко первый и никакой другой. То же самое относилось н кзаписн. Все это напоминает одноразовую кассету без возможности перемотки. Посмотрел фильм - и выбро сил.

Ясно, что так жить нельзя , и в файлах должна быть возможность перемеп1е1П1я назад и вперед в любое разумное место. К счастью, такая возможность npncyuia файлам изначально, только мы о ней еще пе знаем.

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

Этот флаг передвигается не только в результате операций чтепия/запнси. Оказывается, есть две функции, seekgC) и seekpO, которые гюзволяют перемещать флаг в пределах файла. Первая функция работает с объектами класса ifstream - это файлы, открытые для чтения (буква g , стоящая в конце имени функции, означает get - брать). Вторая используется объектами класса ofstream, то есть файлами, открытыми для .записи (буква р ,стоящая в конце имени функции, означает put - класть). С объектами класса fstream могут работать обе функции. Различные перемещения внутри файла демонстрирует программа из листинга 12.11.

Листинг 12.11

#inc1ude <fstream> #include <iostream>



228 Глава 12 ВвОд-вывод

читаются сразу четыре байта, флаг перемешается на четыре позиции к концу файла. Рассмотрим интересный пример такого перемещения - бинарный вывод чисел.

До сих пор мы занимались выводом символов. Если, скажем, в целочисленной переменной i записано число 1 234 567, то инструкция out i выведет в файл out семь символов: 1,2,3,7. Но если записать в файл содержимое компьютерной памяти, которую занимает переменная i, там окажется всего четыре байта - столько отводит наш компилятор целочисленной переменной. Если теперь переписать эти байты из файла в память, занимаемую другой целочисленной переменной, то в ней окажется то же число. Сказанное иллюстрирует листинг 12.12.

Листинг 12.12

#1 ncl Ude <fstreanT> #i ncl ude <iostreani> using namespace std: int main(){ int i=1234567.j=0:

fstream f( test .ios::inlios::out ios::binary):

f.write(reinterpret cast<char*>(&i).sizeof(i)):

f.seekg(-sizeof(1nt).i OS::cur);

f.read(reinterpret cast<char*>(&j).sizeof(int)):

cout j endl:

f.closeO;

return 0:

Открытие файла f в комментариях не нуждается, чего нельзя сказать об инструкции записи f.writeO. Чтобы в ней разобраться, вспомним прототип функции:

writeCchar *.int):

Бинарный вывод экономит место в файле, если число превышает 9999 (подумайте почему).

Непоследовательные файлы 229

Первый параметр - адрес начала последовательности байтов, второй - число этих байтов. В нашем случае число равно размеру переменной int,то есть sizeof (int). Но вот адреса начала последовательности из четырех байтов, где хранится переменная int, у нас нет. Казалось бы, этот адрес равен &1, но &1 - указатель на int, а не на char. Чтобы получить из &i требуемый адрес, необходимо явное приведение типов. Однако оператор static cast<char *> в этом случае не подходит, потому что преобразование указателя на i nt в указатель на char компилятор считает опасным и просто отказывается это делать. Вот для таких рисковагшых трансформаций и предназначен оператор reinterpret cast<>(). Он - настоящий волшебник, сгюсобный превратить указатель в целое число, а людоеда в мышь. Поэтому пользоваться им следует с осторожностью.

Но в нашем случае никакого риска нет, и операция записи работает правильно. В этом можно убедиться, прочитав число из файла и посмотрев его на экране. Для этого нужно, прежде всего, вернуть указатель файла на si zeof (int) позиций назад, что и делает инструкция:

f.seekg(-sizeof(int).ios::сиг):

Далее файл читается инструкцией f .readO, причем для большей убедительности содержимое файла переписывается не в исходную, а в другую переменную j. Ее адрес вычисляется так же, как и при записи файла. Ну и, наконец, инструкция cout j endl выводит на экран число 1 234 567 - то самое, что мы и ожидали увидеть.

Любопь1тио посмотреть па содержимое получившегося файла. Для этого в оболочке FAR файл test сначала подсвечивается, затем нажимается кнопка (или клавиша) F3 (просмотр) и далее - F4 (показ в шестнадцате-ричных кодах). Наше число выглядит так:

87 06 12 00



1 ... 30 31 32 [ 33 ] 34 35 36 37

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика