|
Программирование >> Унарные и бинарные операторы
Как уже говорилось, ввод-вывод данных основан на потоках ввода-вывода - последовательностях символов. Такой последовательностью можно считать и файл, поэтому к файлам применимы те же функции, операторы и манипуляторы, что и к объектам cin и cout. Формально это происходит потому, что вся система ввода-вывода в С++ построена иерархически. На вершине стоит класс ios, от которого происходят классы istream и ostream, порождающие такие объекты-, как cin и cout. В свою очередь, файлы в С++ описываются классами i fstream, of stream и f stream, производными от i stream и ofstrefm. Вот почему они так похожи па обычные потоки. Посмотрим, например, как выводятся в файл целые числа и как они читаются из ({)айла Профамма, которая это проделывает, показана в листигп-е 12.6. вен нулю, когда переменная i равна 9,19,29 и т. д. То есть профамма выводит элемент массива под номером 9,19, 29, ... и т. д., а затем переводит строку - именно этого мы и добиваемся! Кроме манипуляторов setwO и endl, упомянутых в этом ра.зделс, есть довольно много других, объявленных в файлах <iomanip> и <iostream>. Заголовочные файлы компилятора - своеобразный справочник по языку, именно они открывают нам устройство стандартных объектов, функций и операторов. Этими файлами обязательно нужно пользоваться, хотя на первых порах они немного пугают. Задача 12.1. Начните знакомство с заголовочными файлами прямо сейчас. Для начала постарайтесь выяснить, какие бывают манипуляторы, как они устроены и что делают. Файлы Решим для примера простую задачу: вывести на экраи сто целых чисел так, чтобы в одной строке их было 10. Естественно, числа на экране должны стоять стройными рядами, образуя что-то вроде таблицы. Эту простую задачу решает программа, показаьн1ая в листинге 12.5. Листинг 12.5 #include <1ostream> #inc1ude <iomanip using namespace std: int main(){ int m[100]: forCint i=0:i<100:i++){ m[i>i: for(int i=0:i<100:i++){ cout setwO) m[i]: if(!((i+l)X10)) cout endl: return 0: } В этой программе на экран выводятся сто идущих подряд чисел, храня1цихся в массиве т[]. Количество позиций, занимаемых на экране одним числом, определяет манипулятор setwO). В нашем случае максимальное выводимое число равно 99, поэтому достаточно трех позиций, чтобы числа стояли ровными столбцами и не сливались. Остается тол ько научиться вовремя переводить строку, чтобы числа образовали прямоугольную таблицу с десятью столбцами. Делает это следуюн1ая инструкция, которая определяет момент перевода строки (когда операция (i+DXlO дает ноль): if(!((i+imO)) cout endl: Оператор %, до сих пор нам не встречавптйся, означает остаток от деления. Например, 100%3 равно 1, потому что 100 - 33 X 3 + 1. В наиюм примере вычисляется остаток от деления i+1 на 10. Очевидно, он ра- Листинг 12.6 #include <iostrearr> #include <fstream> #include <lomanip> using namespace std; int main(){ int a[100]: ofstream outCdigs ); forCint i=0;i<100:i++) out setwO) i: out.closeO; ifstream inCdigs ): forCint 1=0;i<lOO;i++) in a[i]: forCint i=0:i<100;i++) cout a[i] endl: in.closeC); return 0; Сначала в этой программе создается объект out класса ofstream. Его конструктору указывается имя файла digs. Класс ofstream описывает файлы, созданные только для записи, что нам и нужно. Запись в файл ста последовательных чисел выполняет цикл: forCint 1=0;1<100:1++) out setw(3) i; В этом цикле объект out ничем, по сути, не отличается от cout, хотя первый описывает файл, а второй - экран монитора. Манипулятор setw(3) здесь необходим, чтобы создать пробелы между последовательными числами. Пробелы показывают оператору , где кончается одно чиаю и начинается другое. После вывода чисел файл digs закрывается функцией out.closeO и тут же открывается вновь. На этот раз с ним связан объект in класса ifstream, то есть файл digs теперь можно только читать, что и делается в цикле: forCint i=0;1<100:i++) in a[i]: После ввода в массиве а[] оказываются те же числа, что и в файле. В этом нас убеждает последний цикл, выводящий числа из массива на экран. Задача 12.2. Скомпилируйте и запустите программу, показанную в листинге 12.6. С помощью текстового редактора посмотрите, что записано в файле digs. То, что файлы происходят от потоков, вовсе не значит, что в них нет ничего нового. Родители часто недоумевают, откуда у них такие дети и в кого. Как мы только что видели, у файлов много общего с родительскими классами, но много и различий, связанных с их иной природой. Файлы устойчивее потоков. Их можно хранить, что-то менять в любой части файла, добавлять данные в конец файла и т. д. Возможные операции с файлом определяются при его открытии. Делается это с помон1ЬЮ специальных констант, определенных в классе ios. До сих пор мы порождали файлы из классов ifstream (для чтения) и ofstream (для записи). Попробуем для разнообразия объявлять файлы как объекты класса f stream, порождающего файлы, пригодные как для чтения, так и для записи. Итак, следующее объявление открывает файл name для чтения, а константа ios::out означает открытие для записи: fstream nameC digs .ios::in): Можно указать сразу несколько констант, разделенных знаком I. Так, показанное ниже объявление открывает файл name как для чтения, так и для записи: fstream nameC digs .ios::in ios::out); Открытие для записи требует осторожности, потому что можно уничтожить уже существующий файл. Именно так действует одинокая константа ios:: out. Файлы 223 цепочку несколько операций, например ci п .getCch) chl. Но условие выполнения цикла while О - это, как мы знаем, булево выражение, принимающее только два значения - true и false. Разрешается то противоречие просто: как только компилятор видит, ч го выражение, в котором есть объекты, принадлежащие системе ввода-вывода, по своему смыслу должно принимать только два значения, true и false, он превращает их в булево значение. Так, например, функция in.getO, попав в круглые скобки после whi 1е(), преврап1ается компилятором в true, если чтение было успешным, и в fal se - в противном случае*. Обычно значение false получается, когда достигнут конец файла. В этом случае цикл whileO прервется, не дойдя до функции putO, потому что последгни ! символ уже прочитан и записан в файл out при предыдуп1см проходе цикла. Теперь попробуем написать программу копирования файлов. Исходя из того, что до сих пор пам известно, выглядеть она будет так, как в листинге 12.8. Листинг 12.8 #include <fstream> using namespace std: int maindnt argc. char **argv){ char ch; fstream in(argv[l]. ios::in); fstream out(argv[2].ios;:out); while(in.get(ch)) out.put(ch); В разделе Питание iipoipa.\iM* главы 4 .мы уже сталкивались с подобным выражением \vhile(cin buf). но. пс имея достаточных зна1И111, просто говорили, что оно равно true или false в зависимости от введенно1о символа. Чтобы сохранить файл, можно открывать его одновременно для чтения и записи или использовать константу ios: :арр, разрешающую запись только в конец файла (режим добавления). Можно также использовать константу ios: :погер1асе, запрещающую открывать файл, если он уже существует. Программа, пок11занная в листинге 12.7, проверяет, есть ли в текущей папке файл с именем digs. Листинг 12.7 #include <iostreani> finclude <fstream> #include <iomanip> using namespace std: int mainC){ fstream out( digs .ios::out ios::norep1ace); if(out.faiH)) cout Файл уже существует endl: out.closeO: return 0: Теперь после знакомства с открытием файлов можно попробовать их копировать с помо1цью собственной версии команды сору операционной системы. Дублировать файлы крайне просто, если воспользоваться их сходством с потоками. Нужно, очевидно, читать символ из одного файла и записывать его в другой. Все это легко проделать в цикле: whi1e(in.get(ch)) out.put(ch): Здесь in - файл-источник, а out - файл-копия. Этот цикл кажется крайне простым и поэтому, быть может, требует длинных пояснений. Удивляет, прежде всего, условие выполнения цикла whi 1е(), ведь in.get(ch), как и всякая операция ввода, возвращает ссылку на объект (в наиюм случае - класса i fstream или fstream). Делается это для того, чтобы можно было соединить в одну
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |