|
Программирование >> Унарные и бинарные операторы
ная. Копировать по байту за раз - большая расточительность. Поэтому создадим напоследок еще одну команду копирования, основанную на функциях 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
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |